logos-sdk 0.0.25.dev11__tar.gz → 0.0.25.dev13__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.dev11 → logos_sdk-0.0.25.dev13}/PKG-INFO +1 -1
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/CampaignManager.py +13 -13
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/DV360.py +17 -17
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/Facebook.py +14 -14
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/GoogleAds.py +9 -10
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/MerchantCenter.py +13 -13
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/MicrosoftAdvertising.py +87 -22
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/Sklik.py +135 -68
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/__init__.py +10 -12
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk.egg-info/PKG-INFO +1 -1
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/LICENSE +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/README.md +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/__init__.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/big_query/BigQuery.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/big_query/__init__.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/logging/LogosLogger.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/logging/__init__.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/Collabim.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/GoogleSheets.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/MarketMiner.py +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk.egg-info/SOURCES.txt +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk.egg-info/dependency_links.txt +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk.egg-info/requires.txt +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk.egg-info/top_level.txt +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/setup.cfg +0 -0
- {logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/setup.py +0 -0
|
@@ -5,8 +5,7 @@ import os
|
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
7
|
from random import randint
|
|
8
|
-
from logos_sdk.services import get_headers
|
|
9
|
-
from requests import request
|
|
8
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
10
9
|
from typing import Dict, List
|
|
11
10
|
from http import HTTPStatus
|
|
12
11
|
from googleapiclient.http import MediaIoBaseDownload, HttpRequest
|
|
@@ -21,6 +20,7 @@ class CampaignManagerServiceException(Exception):
|
|
|
21
20
|
class CampaignManagerService:
|
|
22
21
|
def __init__(self, url: str = None):
|
|
23
22
|
load_dotenv()
|
|
23
|
+
self.session = get_retry_session()
|
|
24
24
|
self._URL = url or os.environ.get("CM360_SERVICE_PATH")
|
|
25
25
|
self._CREATE_REPORT = self._URL + "/create-report"
|
|
26
26
|
self._GET_REPORT = self._URL + "/get-report"
|
|
@@ -67,7 +67,7 @@ class CampaignManagerService:
|
|
|
67
67
|
"secret_id": secret_id,
|
|
68
68
|
}
|
|
69
69
|
header = get_headers(self._CREATE_REPORT)
|
|
70
|
-
response = request(
|
|
70
|
+
response = self.session.request(
|
|
71
71
|
method="post", url=self._CREATE_REPORT, json=body, headers=header
|
|
72
72
|
)
|
|
73
73
|
|
|
@@ -92,7 +92,7 @@ class CampaignManagerService:
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
header = get_headers(self._GET_REPORT)
|
|
95
|
-
response = request("post", url=self._GET_REPORT, json=body, headers=header)
|
|
95
|
+
response = self.session.request("post", url=self._GET_REPORT, json=body, headers=header)
|
|
96
96
|
|
|
97
97
|
if response.status_code == HTTPStatus.OK:
|
|
98
98
|
service_response = response.json()
|
|
@@ -114,7 +114,7 @@ class CampaignManagerService:
|
|
|
114
114
|
"secret_id": secret_id,
|
|
115
115
|
}
|
|
116
116
|
header = get_headers(self._RUN_REPORT)
|
|
117
|
-
response = request("post", url=self._RUN_REPORT, json=body, headers=header)
|
|
117
|
+
response = self.session.request("post", url=self._RUN_REPORT, json=body, headers=header)
|
|
118
118
|
service_response = response.json()
|
|
119
119
|
|
|
120
120
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -173,7 +173,7 @@ class CampaignManagerService:
|
|
|
173
173
|
"""
|
|
174
174
|
body = {"report_id": report_id, "file_id": file_id, "secret_id": secret_id}
|
|
175
175
|
header = get_headers(self._GET_FILE)
|
|
176
|
-
response = request("post", url=self._GET_FILE, json=body, headers=header)
|
|
176
|
+
response = self.session.request("post", url=self._GET_FILE, json=body, headers=header)
|
|
177
177
|
|
|
178
178
|
if response.status_code == HTTPStatus.OK:
|
|
179
179
|
service_response = response.json()
|
|
@@ -215,7 +215,7 @@ class CampaignManagerService:
|
|
|
215
215
|
header = get_headers(self._GET_FILE_MEDIA_REQUEST)
|
|
216
216
|
|
|
217
217
|
# fetch the authorized request from our service for downloading the report from API
|
|
218
|
-
response = request(
|
|
218
|
+
response = self.session.request(
|
|
219
219
|
"post", url=self._GET_FILE_MEDIA_REQUEST, json=body, headers=header
|
|
220
220
|
)
|
|
221
221
|
|
|
@@ -283,7 +283,7 @@ class CampaignManagerService:
|
|
|
283
283
|
"secret_id": secret_id,
|
|
284
284
|
}
|
|
285
285
|
header = get_headers(self._DELETE_REPORT)
|
|
286
|
-
response = request("post", url=self._DELETE_REPORT, json=body, headers=header)
|
|
286
|
+
response = self.session.request("post", url=self._DELETE_REPORT, json=body, headers=header)
|
|
287
287
|
|
|
288
288
|
if response.status_code == HTTPStatus.OK:
|
|
289
289
|
return True
|
|
@@ -325,7 +325,7 @@ class CampaignManagerService:
|
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
header = get_headers(self._QUERY_DIMENSION_VALUES)
|
|
328
|
-
response = request(
|
|
328
|
+
response = self.session.request(
|
|
329
329
|
"post", url=self._QUERY_DIMENSION_VALUES, json=body, headers=header
|
|
330
330
|
)
|
|
331
331
|
|
|
@@ -338,7 +338,7 @@ class CampaignManagerService:
|
|
|
338
338
|
# if there was a last page response is empty string
|
|
339
339
|
while service_response["data"]["nextPageToken"]:
|
|
340
340
|
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
341
|
-
response = request(
|
|
341
|
+
response = self.session.request(
|
|
342
342
|
"post", url=self._QUERY_DIMENSION_VALUES, json=body, headers=header
|
|
343
343
|
)
|
|
344
344
|
|
|
@@ -411,7 +411,7 @@ class CampaignManagerService:
|
|
|
411
411
|
header = get_headers(self._GET_ACCESSIBLE_PARENT_ACCOUNTS)
|
|
412
412
|
body = {"secret_id": secret_id}
|
|
413
413
|
|
|
414
|
-
response = request(
|
|
414
|
+
response = self.session.request(
|
|
415
415
|
"post", url=self._GET_ACCESSIBLE_PARENT_ACCOUNTS, json=body, headers=header
|
|
416
416
|
)
|
|
417
417
|
|
|
@@ -436,7 +436,7 @@ class CampaignManagerService:
|
|
|
436
436
|
},
|
|
437
437
|
}
|
|
438
438
|
|
|
439
|
-
response = request(
|
|
439
|
+
response = self.session.request(
|
|
440
440
|
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
441
441
|
)
|
|
442
442
|
|
|
@@ -460,7 +460,7 @@ class CampaignManagerService:
|
|
|
460
460
|
"advertiser_id": advertiser_id
|
|
461
461
|
}
|
|
462
462
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
463
|
-
response = request(
|
|
463
|
+
response = self.session.request(
|
|
464
464
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
465
465
|
)
|
|
466
466
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
from logos_sdk.services import get_headers
|
|
2
|
-
from requests import request
|
|
1
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
3
2
|
from http import HTTPStatus
|
|
4
3
|
from time import sleep
|
|
5
4
|
from random import randint
|
|
@@ -17,6 +16,7 @@ class DV360ServiceException(Exception):
|
|
|
17
16
|
class DV360Service:
|
|
18
17
|
def __init__(self, url=None):
|
|
19
18
|
load_dotenv()
|
|
19
|
+
self.session = get_retry_session()
|
|
20
20
|
self._URL = url or os.environ.get("DV360_SERVICE_PATH")
|
|
21
21
|
self._LIST_LINE_ITEMS = self._URL + "/line-items"
|
|
22
22
|
self._BULK_LIST_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS = (
|
|
@@ -52,7 +52,7 @@ class DV360Service:
|
|
|
52
52
|
if filter_string is not None:
|
|
53
53
|
body["filter"] = filter_string
|
|
54
54
|
|
|
55
|
-
response = request("post", url=self._LIST_LINE_ITEMS, json=body, headers=header)
|
|
55
|
+
response = self.session.request("post", url=self._LIST_LINE_ITEMS, json=body, headers=header)
|
|
56
56
|
|
|
57
57
|
if response.status_code == HTTPStatus.OK:
|
|
58
58
|
service_response = response.json()
|
|
@@ -75,7 +75,7 @@ class DV360Service:
|
|
|
75
75
|
:param filter_string: Allows filtering by line item fields
|
|
76
76
|
:return List of AssignedTargetingOption objects
|
|
77
77
|
"""
|
|
78
|
-
header = get_headers(self.
|
|
78
|
+
header = get_headers(self._BULK_LIST_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS)
|
|
79
79
|
body = {
|
|
80
80
|
"advertiser_id": advertiser_id,
|
|
81
81
|
"secret_id": secret_id,
|
|
@@ -83,7 +83,7 @@ class DV360Service:
|
|
|
83
83
|
"filter": filter_string,
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
response = request(
|
|
86
|
+
response = self.session.request(
|
|
87
87
|
"post",
|
|
88
88
|
url=self._BULK_LIST_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS,
|
|
89
89
|
json=body,
|
|
@@ -128,7 +128,7 @@ class DV360Service:
|
|
|
128
128
|
if create_requests is not None:
|
|
129
129
|
body["create_requests"] = create_requests
|
|
130
130
|
|
|
131
|
-
response = request(
|
|
131
|
+
response = self.session.request(
|
|
132
132
|
"post",
|
|
133
133
|
url=self._BULK_EDIT_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS,
|
|
134
134
|
json=body,
|
|
@@ -152,7 +152,7 @@ class DV360Service:
|
|
|
152
152
|
header = get_headers(self._CREATE_CHANNEL)
|
|
153
153
|
body = {"advertiser_id": advertiser_id, "secret_id": secret_id, "name": name}
|
|
154
154
|
|
|
155
|
-
response = request("post", url=self._CREATE_CHANNEL, json=body, headers=header)
|
|
155
|
+
response = self.session.request("post", url=self._CREATE_CHANNEL, json=body, headers=header)
|
|
156
156
|
|
|
157
157
|
if response.status_code == HTTPStatus.OK:
|
|
158
158
|
service_response = response.json()
|
|
@@ -174,7 +174,7 @@ class DV360Service:
|
|
|
174
174
|
if filter_string is not None:
|
|
175
175
|
body["filter"] = filter_string
|
|
176
176
|
|
|
177
|
-
response = request("post", url=self._LIST_CHANNELS, json=body, headers=header)
|
|
177
|
+
response = self.session.request("post", url=self._LIST_CHANNELS, json=body, headers=header)
|
|
178
178
|
|
|
179
179
|
if response.status_code == HTTPStatus.OK:
|
|
180
180
|
service_response = response.json()
|
|
@@ -203,7 +203,7 @@ class DV360Service:
|
|
|
203
203
|
if filter_string is not None:
|
|
204
204
|
body["filter"] = filter_string
|
|
205
205
|
|
|
206
|
-
response = request(
|
|
206
|
+
response = self.session.request(
|
|
207
207
|
"post", url=self._LIST_CHANNEL_SITES, json=body, headers=header
|
|
208
208
|
)
|
|
209
209
|
|
|
@@ -243,7 +243,7 @@ class DV360Service:
|
|
|
243
243
|
if created_sites is not None:
|
|
244
244
|
body["created_sites"] = created_sites
|
|
245
245
|
|
|
246
|
-
response = request(
|
|
246
|
+
response = self.session.request(
|
|
247
247
|
"post",
|
|
248
248
|
url=self._BULK_EDIT_CHANNEL_SITES,
|
|
249
249
|
json=body,
|
|
@@ -277,7 +277,7 @@ class DV360Service:
|
|
|
277
277
|
"metrics_names": metrics_names,
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
response = request("post", url=self._CREATE_QUERY, json=body, headers=header)
|
|
280
|
+
response = self.session.request("post", url=self._CREATE_QUERY, json=body, headers=header)
|
|
281
281
|
|
|
282
282
|
if response.status_code == HTTPStatus.OK:
|
|
283
283
|
service_response = response.json()
|
|
@@ -296,7 +296,7 @@ class DV360Service:
|
|
|
296
296
|
header = get_headers(self._RUN_QUERY)
|
|
297
297
|
body = {"secret_id": secret_id, "query_id": query_id}
|
|
298
298
|
|
|
299
|
-
response = request("post", url=self._RUN_QUERY, json=body, headers=header)
|
|
299
|
+
response = self.session.request("post", url=self._RUN_QUERY, json=body, headers=header)
|
|
300
300
|
|
|
301
301
|
if response.status_code == HTTPStatus.OK:
|
|
302
302
|
service_response = response.json()
|
|
@@ -319,7 +319,7 @@ class DV360Service:
|
|
|
319
319
|
},
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
-
response = request(
|
|
322
|
+
response = self.session.request(
|
|
323
323
|
"post", url=self._GET_ACCESSIBLE_PARTNERS, json=body, headers=header
|
|
324
324
|
)
|
|
325
325
|
|
|
@@ -344,7 +344,7 @@ class DV360Service:
|
|
|
344
344
|
},
|
|
345
345
|
}
|
|
346
346
|
|
|
347
|
-
response = request(
|
|
347
|
+
response = self.session.request(
|
|
348
348
|
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
349
349
|
)
|
|
350
350
|
|
|
@@ -366,7 +366,7 @@ class DV360Service:
|
|
|
366
366
|
"advertiser_id": advertiser_id
|
|
367
367
|
}
|
|
368
368
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
369
|
-
response = request(
|
|
369
|
+
response = self.session.request(
|
|
370
370
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
371
371
|
)
|
|
372
372
|
|
|
@@ -408,7 +408,7 @@ class DV360Service:
|
|
|
408
408
|
header = get_headers(self._GET_QUERY_METADATA)
|
|
409
409
|
body = {"secret_id": secret_id, "query_id": query_id, "report_id": report_id}
|
|
410
410
|
|
|
411
|
-
response = request(
|
|
411
|
+
response = self.session.request(
|
|
412
412
|
"post", url=self._GET_QUERY_METADATA, json=body, headers=header
|
|
413
413
|
)
|
|
414
414
|
|
|
@@ -464,7 +464,7 @@ class DV360Service:
|
|
|
464
464
|
header = get_headers(self._DELETE_QUERY)
|
|
465
465
|
body = {"secret_id": secret_id, "query_id": query_id}
|
|
466
466
|
|
|
467
|
-
response = request("post", url=self._DELETE_QUERY, json=body, headers=header)
|
|
467
|
+
response = self.session.request("post", url=self._DELETE_QUERY, json=body, headers=header)
|
|
468
468
|
|
|
469
469
|
if response.status_code == HTTPStatus.OK:
|
|
470
470
|
return True
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
from requests import request
|
|
2
1
|
from typing import List, Dict, Optional
|
|
3
|
-
from logos_sdk.services import get_headers
|
|
2
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
4
3
|
from http import HTTPStatus
|
|
5
4
|
from random import randint
|
|
6
5
|
from dotenv import load_dotenv
|
|
@@ -15,6 +14,7 @@ class FacebookServiceException(Exception):
|
|
|
15
14
|
class FacebookService:
|
|
16
15
|
def __init__(self, url=None):
|
|
17
16
|
load_dotenv()
|
|
17
|
+
self.session = get_retry_session()
|
|
18
18
|
self._URL = url or os.environ.get("FACEBOOK_SERVICE_PATH")
|
|
19
19
|
self._ACCESSIBLE_ACCOUNTS = self._URL + "/accessible-accounts"
|
|
20
20
|
self._INSIGHTS = self._URL + "/insights"
|
|
@@ -48,7 +48,7 @@ class FacebookService:
|
|
|
48
48
|
|
|
49
49
|
header = get_headers(self._ACCESSIBLE_ACCOUNTS)
|
|
50
50
|
|
|
51
|
-
response = request(
|
|
51
|
+
response = self.session.request(
|
|
52
52
|
"post",
|
|
53
53
|
url=self._ACCESSIBLE_ACCOUNTS,
|
|
54
54
|
json=body,
|
|
@@ -74,7 +74,7 @@ class FacebookService:
|
|
|
74
74
|
"account_id": account_id
|
|
75
75
|
}
|
|
76
76
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
77
|
-
response = request(
|
|
77
|
+
response = self.session.request(
|
|
78
78
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
79
79
|
)
|
|
80
80
|
|
|
@@ -121,7 +121,7 @@ class FacebookService:
|
|
|
121
121
|
body["level"] = level
|
|
122
122
|
header = get_headers(self._INSIGHTS)
|
|
123
123
|
|
|
124
|
-
response = request(
|
|
124
|
+
response = self.session.request(
|
|
125
125
|
"post", url=self._INSIGHTS, json=body, headers=header, timeout=timeout
|
|
126
126
|
)
|
|
127
127
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -162,7 +162,7 @@ class FacebookService:
|
|
|
162
162
|
|
|
163
163
|
header = get_headers(self._LINK_URLS)
|
|
164
164
|
|
|
165
|
-
response = request(
|
|
165
|
+
response = self.session.request(
|
|
166
166
|
"post", url=self._LINK_URLS, json=body, headers=header, timeout=timeout
|
|
167
167
|
)
|
|
168
168
|
|
|
@@ -187,7 +187,7 @@ class FacebookService:
|
|
|
187
187
|
|
|
188
188
|
header = get_headers(self._ACCESSIBLE_BUSINESSES)
|
|
189
189
|
|
|
190
|
-
response = request(
|
|
190
|
+
response = self.session.request(
|
|
191
191
|
"post",
|
|
192
192
|
url=self._ACCESSIBLE_BUSINESSES,
|
|
193
193
|
json=body,
|
|
@@ -226,7 +226,7 @@ class FacebookService:
|
|
|
226
226
|
|
|
227
227
|
header = get_headers(self._PRODUCT_CATALOGS)
|
|
228
228
|
|
|
229
|
-
response = request(
|
|
229
|
+
response = self.session.request(
|
|
230
230
|
"post",
|
|
231
231
|
url=self._PRODUCT_CATALOGS,
|
|
232
232
|
json=body,
|
|
@@ -262,7 +262,7 @@ class FacebookService:
|
|
|
262
262
|
|
|
263
263
|
header = get_headers(self._PRODUCT_CATALOG)
|
|
264
264
|
|
|
265
|
-
response = request(
|
|
265
|
+
response = self.session.request(
|
|
266
266
|
"post",
|
|
267
267
|
url=self._PRODUCT_CATALOG,
|
|
268
268
|
json=body,
|
|
@@ -304,7 +304,7 @@ class FacebookService:
|
|
|
304
304
|
|
|
305
305
|
header = get_headers(self._PRODUCTS)
|
|
306
306
|
|
|
307
|
-
response = request(
|
|
307
|
+
response = self.session.request(
|
|
308
308
|
"post", url=self._PRODUCTS, json=body, headers=header, timeout=timeout
|
|
309
309
|
)
|
|
310
310
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -325,7 +325,7 @@ class FacebookService:
|
|
|
325
325
|
body = {"secret_id": secret_id, "catalog_id": catalog_id}
|
|
326
326
|
|
|
327
327
|
header = get_headers(self._FEEDS)
|
|
328
|
-
response = request(
|
|
328
|
+
response = self.session.request(
|
|
329
329
|
"post", url=self._FEEDS, json=body, headers=header, timeout=timeout
|
|
330
330
|
)
|
|
331
331
|
|
|
@@ -348,7 +348,7 @@ class FacebookService:
|
|
|
348
348
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
349
349
|
|
|
350
350
|
header = get_headers(self._FEED_ERRORS)
|
|
351
|
-
response = request(
|
|
351
|
+
response = self.session.request(
|
|
352
352
|
"post", url=self._FEED_ERRORS, json=body, headers=header, timeout=timeout
|
|
353
353
|
)
|
|
354
354
|
|
|
@@ -370,7 +370,7 @@ class FacebookService:
|
|
|
370
370
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
371
371
|
|
|
372
372
|
header = get_headers(self._FEED_ERRORS_REPORT_STATUS)
|
|
373
|
-
response = request(
|
|
373
|
+
response = self.session.request(
|
|
374
374
|
"post",
|
|
375
375
|
url=self._FEED_ERRORS_REPORT_STATUS,
|
|
376
376
|
json=body,
|
|
@@ -398,7 +398,7 @@ class FacebookService:
|
|
|
398
398
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
399
399
|
|
|
400
400
|
header = get_headers(self._FEED_ERRORS_REPORT)
|
|
401
|
-
response = request(
|
|
401
|
+
response = self.session.request(
|
|
402
402
|
"post",
|
|
403
403
|
url=self._FEED_ERRORS_REPORT,
|
|
404
404
|
json=body,
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from requests import request
|
|
2
1
|
from requests.exceptions import Timeout
|
|
3
2
|
from typing import List, Union, Dict
|
|
4
|
-
from logos_sdk.services import get_headers
|
|
3
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
5
4
|
from http import HTTPStatus
|
|
6
5
|
from dotenv import load_dotenv
|
|
7
6
|
import os
|
|
@@ -16,6 +15,7 @@ class GoogleAdsServiceException(Exception):
|
|
|
16
15
|
class GoogleAdsService:
|
|
17
16
|
def __init__(self, url=None):
|
|
18
17
|
load_dotenv()
|
|
18
|
+
self.session = get_retry_session()
|
|
19
19
|
self._URL = url or os.environ.get("GOOGLE_ADS_SERVICE_PATH")
|
|
20
20
|
self._SEARCH_STREAM = self._URL + "/search-stream"
|
|
21
21
|
self._SEARCH = self._URL + "/search"
|
|
@@ -24,11 +24,10 @@ class GoogleAdsService:
|
|
|
24
24
|
self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/list-accessible-accounts"
|
|
25
25
|
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
def fetch_with_retry_on_timeout(url, json, headers):
|
|
27
|
+
def fetch_with_retry_on_timeout(self,url, json, headers):
|
|
29
28
|
for attempt in range(5):
|
|
30
29
|
try:
|
|
31
|
-
return request("post", url, json=json, headers=headers, timeout=25)
|
|
30
|
+
return self.session.request("post", url, json=json, headers=headers, timeout=25)
|
|
32
31
|
except Timeout:
|
|
33
32
|
delay = 2 * (2 ** attempt) + random.randint(0, 9)
|
|
34
33
|
print(
|
|
@@ -103,7 +102,7 @@ class GoogleAdsService:
|
|
|
103
102
|
# if there was a last page response is empty string
|
|
104
103
|
while service_response["data"]["next_page_token"]:
|
|
105
104
|
body["page_token"] = service_response["data"]["next_page_token"]
|
|
106
|
-
response = request("post", url=self._SEARCH, json=body, headers=header)
|
|
105
|
+
response = self.session.request("post", url=self._SEARCH, json=body, headers=header)
|
|
107
106
|
|
|
108
107
|
if response.status_code != HTTPStatus.OK:
|
|
109
108
|
raise GoogleAdsServiceException(response.content)
|
|
@@ -130,7 +129,7 @@ class GoogleAdsService:
|
|
|
130
129
|
}
|
|
131
130
|
|
|
132
131
|
header = get_headers(self._EXCLUDE_FOR_ACCOUNT)
|
|
133
|
-
response = request(
|
|
132
|
+
response = self.session.request(
|
|
134
133
|
"post", url=self._EXCLUDE_FOR_ACCOUNT, json=body, headers=header
|
|
135
134
|
)
|
|
136
135
|
|
|
@@ -163,7 +162,7 @@ class GoogleAdsService:
|
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
header = get_headers(self._EXCLUDE_FOR_AD_GROUP)
|
|
166
|
-
response = request(
|
|
165
|
+
response = self.session.request(
|
|
167
166
|
"post", url=self._EXCLUDE_FOR_AD_GROUP, json=body, headers=header
|
|
168
167
|
)
|
|
169
168
|
|
|
@@ -199,7 +198,7 @@ class GoogleAdsService:
|
|
|
199
198
|
|
|
200
199
|
while service_response["data"]["next_page_token"]:
|
|
201
200
|
body["page_token"] = service_response["data"]["next_page_token"]
|
|
202
|
-
response = request(
|
|
201
|
+
response = self.session.request(
|
|
203
202
|
"post", url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
204
203
|
)
|
|
205
204
|
|
|
@@ -223,7 +222,7 @@ class GoogleAdsService:
|
|
|
223
222
|
"account_id": account_id
|
|
224
223
|
}
|
|
225
224
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
226
|
-
response = request(
|
|
225
|
+
response = self.session.request(
|
|
227
226
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
228
227
|
)
|
|
229
228
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from http import HTTPStatus
|
|
2
|
-
from
|
|
3
|
-
from logos_sdk.services import get_headers
|
|
2
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
4
3
|
from dotenv import load_dotenv
|
|
5
4
|
import os
|
|
6
5
|
|
|
@@ -12,6 +11,7 @@ class MerchantServiceException(Exception):
|
|
|
12
11
|
class MerchantCenterService:
|
|
13
12
|
def __init__(self, url=None):
|
|
14
13
|
load_dotenv()
|
|
14
|
+
self.session = get_retry_session()
|
|
15
15
|
self._URL = url or os.environ.get("MERCHANT_CENTER_SERVICE_PATH")
|
|
16
16
|
self._LIST_ACCOUNTS = self._URL + "/account-service/accounts"
|
|
17
17
|
self._LIST_ACCESSIBLE_ACCOUNTS = self._URL + "/account-service/list-accessible-accounts"
|
|
@@ -30,7 +30,7 @@ class MerchantCenterService:
|
|
|
30
30
|
"""
|
|
31
31
|
body = {"merchant_account_id": merchant_account_id, "secret_id": secret_id}
|
|
32
32
|
header = get_headers(self._LIST_ACCOUNTS)
|
|
33
|
-
response = request("post", url=self._LIST_ACCOUNTS, json=body, headers=header)
|
|
33
|
+
response = self.session.request("post", url=self._LIST_ACCOUNTS, json=body, headers=header)
|
|
34
34
|
|
|
35
35
|
if response.status_code == HTTPStatus.OK:
|
|
36
36
|
service_response = response.json()
|
|
@@ -46,7 +46,7 @@ class MerchantCenterService:
|
|
|
46
46
|
"""
|
|
47
47
|
body = {"secret_id": secret_id}
|
|
48
48
|
header = get_headers(self._LIST_ACCESSIBLE_ACCOUNTS)
|
|
49
|
-
response = request("post", url=self._LIST_ACCESSIBLE_ACCOUNTS, json=body, headers=header)
|
|
49
|
+
response = self.session.request("post", url=self._LIST_ACCESSIBLE_ACCOUNTS, json=body, headers=header)
|
|
50
50
|
|
|
51
51
|
if response.status_code == HTTPStatus.OK:
|
|
52
52
|
service_response = response.json()
|
|
@@ -70,7 +70,7 @@ class MerchantCenterService:
|
|
|
70
70
|
"secret_id": secret_id,
|
|
71
71
|
}
|
|
72
72
|
header = get_headers(self._LIST_ACCOUNT_STATUSES)
|
|
73
|
-
response = request(
|
|
73
|
+
response = self.session.request(
|
|
74
74
|
"post", url=self._LIST_ACCOUNT_STATUSES, json=body, headers=header
|
|
75
75
|
)
|
|
76
76
|
|
|
@@ -90,7 +90,7 @@ class MerchantCenterService:
|
|
|
90
90
|
"""
|
|
91
91
|
body = {"merchant_account_id": merchant_account_id, "secret_id": secret_id, "page_size": page_size}
|
|
92
92
|
header = get_headers(self._LIST_PRODUCTS)
|
|
93
|
-
response = request("post", url=self._LIST_PRODUCTS, json=body, headers=header)
|
|
93
|
+
response = self.session.request("post", url=self._LIST_PRODUCTS, json=body, headers=header)
|
|
94
94
|
|
|
95
95
|
if response.status_code != HTTPStatus.OK:
|
|
96
96
|
raise MerchantServiceException(response.content)
|
|
@@ -100,7 +100,7 @@ class MerchantCenterService:
|
|
|
100
100
|
|
|
101
101
|
while service_response["data"]["nextPageToken"] is not None:
|
|
102
102
|
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
103
|
-
response = request(
|
|
103
|
+
response = self.session.request(
|
|
104
104
|
"post", url=self._LIST_PRODUCTS, json=body, headers=header
|
|
105
105
|
)
|
|
106
106
|
|
|
@@ -121,7 +121,7 @@ class MerchantCenterService:
|
|
|
121
121
|
"""
|
|
122
122
|
body = {"merchant_account_id": merchant_account_id, "secret_id": secret_id, "page_size": page_size}
|
|
123
123
|
header = get_headers(self._LIST_PRODUCT_STATUSES)
|
|
124
|
-
response = request(
|
|
124
|
+
response = self.session.request(
|
|
125
125
|
"post", url=self._LIST_PRODUCT_STATUSES, json=body, headers=header
|
|
126
126
|
)
|
|
127
127
|
|
|
@@ -133,7 +133,7 @@ class MerchantCenterService:
|
|
|
133
133
|
|
|
134
134
|
while service_response["data"]["nextPageToken"] is not None:
|
|
135
135
|
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
136
|
-
response = request(
|
|
136
|
+
response = self.session.request(
|
|
137
137
|
"post", url=self._LIST_PRODUCT_STATUSES, json=body, headers=header
|
|
138
138
|
)
|
|
139
139
|
|
|
@@ -159,7 +159,7 @@ class MerchantCenterService:
|
|
|
159
159
|
"page_size": page_size,
|
|
160
160
|
}
|
|
161
161
|
header = get_headers(self._REPORTS_SEARCH)
|
|
162
|
-
response = request("post", url=self._REPORTS_SEARCH, json=body, headers=header)
|
|
162
|
+
response = self.session.request("post", url=self._REPORTS_SEARCH, json=body, headers=header)
|
|
163
163
|
|
|
164
164
|
if response.status_code == HTTPStatus.OK:
|
|
165
165
|
service_response = response.json()
|
|
@@ -181,7 +181,7 @@ class MerchantCenterService:
|
|
|
181
181
|
"page_size": page_size,
|
|
182
182
|
}
|
|
183
183
|
header = get_headers(self._REPORTS_SEARCH)
|
|
184
|
-
response = request("post", url=self._REPORTS_SEARCH, json=body, headers=header)
|
|
184
|
+
response = self.session.request("post", url=self._REPORTS_SEARCH, json=body, headers=header)
|
|
185
185
|
|
|
186
186
|
if response.status_code != HTTPStatus.OK:
|
|
187
187
|
raise MerchantServiceException(response.content)
|
|
@@ -191,7 +191,7 @@ class MerchantCenterService:
|
|
|
191
191
|
|
|
192
192
|
while service_response["data"]["nextPageToken"] is not None:
|
|
193
193
|
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
194
|
-
response = request(
|
|
194
|
+
response = self.session.request(
|
|
195
195
|
"post", url=self._REPORTS_SEARCH, json=body, headers=header
|
|
196
196
|
)
|
|
197
197
|
|
|
@@ -215,7 +215,7 @@ class MerchantCenterService:
|
|
|
215
215
|
"account_id": account_id
|
|
216
216
|
}
|
|
217
217
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
218
|
-
response = request(
|
|
218
|
+
response = self.session.request(
|
|
219
219
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
220
220
|
)
|
|
221
221
|
|
{logos_sdk-0.0.25.dev11 → logos_sdk-0.0.25.dev13}/logos_sdk/services/MicrosoftAdvertising.py
RENAMED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from http import HTTPStatus
|
|
2
|
-
from
|
|
3
|
-
from logos_sdk.services import get_headers
|
|
2
|
+
from logos_sdk.services import get_headers, get_retry_session
|
|
4
3
|
from typing import List
|
|
5
4
|
from dotenv import load_dotenv
|
|
6
5
|
import os
|
|
@@ -18,16 +17,28 @@ class EntityStatus:
|
|
|
18
17
|
PAUSED = "Paused"
|
|
19
18
|
|
|
20
19
|
|
|
20
|
+
class BidMatchType:
|
|
21
|
+
EXACT = "Exact"
|
|
22
|
+
PHRASE = "Phrase"
|
|
23
|
+
BROAD = "Broad"
|
|
24
|
+
|
|
25
|
+
|
|
21
26
|
class MicrosoftAdvertising:
|
|
22
27
|
def __init__(self, url=None):
|
|
23
28
|
load_dotenv()
|
|
29
|
+
self.session = get_retry_session()
|
|
24
30
|
self._URL = url or os.environ.get("MICROSOFT_ADVERTISING_PATH")
|
|
25
31
|
self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/accessible-accounts"
|
|
26
32
|
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
27
33
|
self._GET_DESTINATION_URL_REPORT = self._URL + "/destination-url-report"
|
|
28
|
-
self._GET_CAMPAIGN_PERFORMANCE_REPORT =
|
|
29
|
-
|
|
34
|
+
self._GET_CAMPAIGN_PERFORMANCE_REPORT = (
|
|
35
|
+
self._URL + "/campaign-performance-report"
|
|
36
|
+
)
|
|
37
|
+
self._GET_GEOGRAPHIC_PERFORMANCE_REPORT = (
|
|
38
|
+
self._URL + "/geographic-performance-report"
|
|
39
|
+
)
|
|
30
40
|
self._GET_BUDGET_SUMMARY_REPORT = self._URL + "/budget-summary-report"
|
|
41
|
+
self._GET_SHARE_OF_VOICE_REPORT = self._URL + "/share-of-voice-report"
|
|
31
42
|
|
|
32
43
|
def get_destination_url_report(
|
|
33
44
|
self,
|
|
@@ -66,7 +77,7 @@ class MicrosoftAdvertising:
|
|
|
66
77
|
} | entity_statuses
|
|
67
78
|
|
|
68
79
|
header = get_headers(self._GET_DESTINATION_URL_REPORT)
|
|
69
|
-
response = request(
|
|
80
|
+
response = self.session.request(
|
|
70
81
|
"post", url=self._GET_DESTINATION_URL_REPORT, json=body, headers=header
|
|
71
82
|
)
|
|
72
83
|
|
|
@@ -112,8 +123,70 @@ class MicrosoftAdvertising:
|
|
|
112
123
|
} | entity_statuses
|
|
113
124
|
|
|
114
125
|
header = get_headers(self._GET_GEOGRAPHIC_PERFORMANCE_REPORT)
|
|
115
|
-
response = request(
|
|
116
|
-
"post",
|
|
126
|
+
response = self.session.request(
|
|
127
|
+
"post",
|
|
128
|
+
url=self._GET_GEOGRAPHIC_PERFORMANCE_REPORT,
|
|
129
|
+
json=body,
|
|
130
|
+
headers=header,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if response.status_code == HTTPStatus.OK:
|
|
134
|
+
service_response = response.json()
|
|
135
|
+
return service_response["data"]
|
|
136
|
+
else:
|
|
137
|
+
raise MicrosoftAdvertisingException(response.content)
|
|
138
|
+
|
|
139
|
+
def get_share_of_voice_report(
|
|
140
|
+
self,
|
|
141
|
+
account_id: str,
|
|
142
|
+
secret_id: str,
|
|
143
|
+
date_from: str,
|
|
144
|
+
date_to: str,
|
|
145
|
+
report_columns: List[str],
|
|
146
|
+
bid_match_type: str = None,
|
|
147
|
+
filter_keywords: List[str] = None,
|
|
148
|
+
entity_statuses: dict = None,
|
|
149
|
+
) -> List:
|
|
150
|
+
"""
|
|
151
|
+
Calls endpoint for getting share of voice stats
|
|
152
|
+
https://learn.microsoft.com/en-us/advertising/reporting-service/geographicperformancereportrequest?view=bingads-13&tabs=xml
|
|
153
|
+
:param account_id: The ID of the account in Microsoft Advertising
|
|
154
|
+
:param secret_id: The ID of the secret in secret manager
|
|
155
|
+
:param date_from: The date we want the report to start from. Must be before date to
|
|
156
|
+
:param date_to: The date we want the report to end at
|
|
157
|
+
:param report_columns: stats we want included in the report
|
|
158
|
+
:param bid_match_type: keywords with this bid match type will be returned
|
|
159
|
+
:param filter_keywords: just keywords from this list will be returned
|
|
160
|
+
:param entity_statuses: dict containing status ad_group_status, campaign_status, keyword_status and account_status
|
|
161
|
+
:return: List of share of voice stats
|
|
162
|
+
"""
|
|
163
|
+
if entity_statuses is None:
|
|
164
|
+
entity_statuses = {
|
|
165
|
+
"ad_group_status": EntityStatus.ACTIVE,
|
|
166
|
+
"campaign_status": EntityStatus.ACTIVE,
|
|
167
|
+
"account_status": EntityStatus.ACTIVE,
|
|
168
|
+
"keyword_status": EntityStatus.ACTIVE,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
body = {
|
|
172
|
+
"account_id": account_id,
|
|
173
|
+
"secret_id": secret_id,
|
|
174
|
+
"date_from": date_from,
|
|
175
|
+
"date_to": date_to,
|
|
176
|
+
"report_columns": report_columns,
|
|
177
|
+
} | entity_statuses
|
|
178
|
+
|
|
179
|
+
if bid_match_type:
|
|
180
|
+
body["bid_match_type"] = bid_match_type
|
|
181
|
+
if filter_keywords:
|
|
182
|
+
body["filter_keywords"] = filter_keywords
|
|
183
|
+
|
|
184
|
+
header = get_headers(self._GET_SHARE_OF_VOICE_REPORT)
|
|
185
|
+
response = self.session.request(
|
|
186
|
+
"post",
|
|
187
|
+
url=self._GET_SHARE_OF_VOICE_REPORT,
|
|
188
|
+
json=body,
|
|
189
|
+
headers=header,
|
|
117
190
|
)
|
|
118
191
|
|
|
119
192
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -157,7 +230,7 @@ class MicrosoftAdvertising:
|
|
|
157
230
|
} | entity_statuses
|
|
158
231
|
|
|
159
232
|
header = get_headers(self._GET_CAMPAIGN_PERFORMANCE_REPORT)
|
|
160
|
-
response = request(
|
|
233
|
+
response = self.session.request(
|
|
161
234
|
"post", url=self._GET_CAMPAIGN_PERFORMANCE_REPORT, json=body, headers=header
|
|
162
235
|
)
|
|
163
236
|
|
|
@@ -173,7 +246,7 @@ class MicrosoftAdvertising:
|
|
|
173
246
|
secret_id: str,
|
|
174
247
|
date_from: str,
|
|
175
248
|
date_to: str,
|
|
176
|
-
report_columns: List[str]
|
|
249
|
+
report_columns: List[str],
|
|
177
250
|
):
|
|
178
251
|
"""
|
|
179
252
|
Calls endpoint for getting budget summary
|
|
@@ -195,7 +268,7 @@ class MicrosoftAdvertising:
|
|
|
195
268
|
}
|
|
196
269
|
|
|
197
270
|
header = get_headers(self._GET_BUDGET_SUMMARY_REPORT)
|
|
198
|
-
response = request(
|
|
271
|
+
response = self.session.request(
|
|
199
272
|
"post", url=self._GET_BUDGET_SUMMARY_REPORT, json=body, headers=header
|
|
200
273
|
)
|
|
201
274
|
|
|
@@ -211,14 +284,9 @@ class MicrosoftAdvertising:
|
|
|
211
284
|
:param secret_id: The ID of the secret in secret manager
|
|
212
285
|
:return: List of dicts with account_id and name keys
|
|
213
286
|
"""
|
|
214
|
-
body = {
|
|
215
|
-
"secret_id": secret_id,
|
|
216
|
-
"filters": {
|
|
217
|
-
"hide_inactive": True
|
|
218
|
-
}
|
|
219
|
-
}
|
|
287
|
+
body = {"secret_id": secret_id, "filters": {"hide_inactive": True}}
|
|
220
288
|
header = get_headers(self._GET_ACCESSIBLE_ACCOUNTS)
|
|
221
|
-
response = request(
|
|
289
|
+
response = self.session.request(
|
|
222
290
|
"post", url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
223
291
|
)
|
|
224
292
|
|
|
@@ -235,12 +303,9 @@ class MicrosoftAdvertising:
|
|
|
235
303
|
:param account_id: The ID of the account in Microsoft Advertising
|
|
236
304
|
:return: True if account has access
|
|
237
305
|
"""
|
|
238
|
-
body = {
|
|
239
|
-
"secret_id": secret_id,
|
|
240
|
-
"account_id": account_id
|
|
241
|
-
}
|
|
306
|
+
body = {"secret_id": secret_id, "account_id": account_id}
|
|
242
307
|
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
243
|
-
response = request(
|
|
308
|
+
response = self.session.request(
|
|
244
309
|
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
245
310
|
)
|
|
246
311
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from typing import List, Dict, Union
|
|
2
|
-
from logos_sdk.services import
|
|
2
|
+
from logos_sdk.services import get_retry_session, get_headers
|
|
3
3
|
from http import HTTPStatus
|
|
4
4
|
|
|
5
5
|
import logging
|
|
@@ -38,7 +38,10 @@ def get_session_if_malformed(wrapped_function):
|
|
|
38
38
|
try:
|
|
39
39
|
return wrapped_function(*args, **kwargs)
|
|
40
40
|
except SklikServiceException as err:
|
|
41
|
-
if
|
|
41
|
+
if (
|
|
42
|
+
json.loads(err.args[0].decode("utf8")).get("detail")
|
|
43
|
+
== "Session has expired or is malformed."
|
|
44
|
+
):
|
|
42
45
|
args[0].get_session(args[0]._secret_id, args[0]._account_email)
|
|
43
46
|
return wrapped_function(*args, **kwargs)
|
|
44
47
|
else:
|
|
@@ -53,8 +56,10 @@ def get_report_results_if_expired(wrapped_function):
|
|
|
53
56
|
try:
|
|
54
57
|
return wrapped_function(*args, **kwargs)
|
|
55
58
|
except SklikServiceException as err:
|
|
56
|
-
if
|
|
57
|
-
|
|
59
|
+
if (
|
|
60
|
+
json.loads(err.args[0].decode("utf8")).get("detail")
|
|
61
|
+
== "Requested report has expired. Please create a new report by calling createReport endpoint."
|
|
62
|
+
):
|
|
58
63
|
print("Report expired, creating new report")
|
|
59
64
|
args[0].get_session(args[0]._secret_id, args[0]._account_email)
|
|
60
65
|
return wrapped_function(*args, **kwargs)
|
|
@@ -67,6 +72,7 @@ def get_report_results_if_expired(wrapped_function):
|
|
|
67
72
|
class SklikService:
|
|
68
73
|
def __init__(self, url=None):
|
|
69
74
|
load_dotenv()
|
|
75
|
+
self.request_session = get_retry_session()
|
|
70
76
|
self._URL = url or os.environ.get("SKLIK_SERVICE_PATH")
|
|
71
77
|
self._CREATE_REPORT = self._URL + "/create-report"
|
|
72
78
|
self._READ_REPORT = self._URL + "/read-report"
|
|
@@ -89,7 +95,10 @@ class SklikService:
|
|
|
89
95
|
:return: Dict
|
|
90
96
|
"""
|
|
91
97
|
body = {"account_email": account_email, "secret_id": secret_id}
|
|
92
|
-
|
|
98
|
+
header = get_headers(self._GET_SESSION)
|
|
99
|
+
response = self.request_session.request(
|
|
100
|
+
"post", url=self._GET_SESSION, json=body, timeout=70, headers=header
|
|
101
|
+
)
|
|
93
102
|
|
|
94
103
|
if response.status_code == HTTPStatus.OK:
|
|
95
104
|
service_response = response.json()
|
|
@@ -109,7 +118,14 @@ class SklikService:
|
|
|
109
118
|
:return: Dict
|
|
110
119
|
"""
|
|
111
120
|
body = {"secret_id": secret_id}
|
|
112
|
-
|
|
121
|
+
header = get_headers(self._GET_SESSION_WITHOUT_ACCOUNT)
|
|
122
|
+
response = self.request_session.request(
|
|
123
|
+
"post",
|
|
124
|
+
url=self._GET_SESSION_WITHOUT_ACCOUNT,
|
|
125
|
+
json=body,
|
|
126
|
+
timeout=70,
|
|
127
|
+
headers=header,
|
|
128
|
+
)
|
|
113
129
|
|
|
114
130
|
if response.status_code == HTTPStatus.OK:
|
|
115
131
|
service_response = response.json()
|
|
@@ -128,22 +144,30 @@ class SklikService:
|
|
|
128
144
|
"""
|
|
129
145
|
try:
|
|
130
146
|
if self.session is not None:
|
|
131
|
-
|
|
147
|
+
header = get_headers(self._LOGOUT)
|
|
148
|
+
self.request_session.request(
|
|
149
|
+
"post",
|
|
150
|
+
url=self._LOGOUT,
|
|
151
|
+
json=self.session,
|
|
152
|
+
timeout=70,
|
|
153
|
+
headers=header,
|
|
154
|
+
)
|
|
155
|
+
|
|
132
156
|
except:
|
|
133
157
|
pass
|
|
134
158
|
|
|
135
159
|
@get_session_if_malformed
|
|
136
160
|
@get_report_results_if_expired
|
|
137
161
|
def get_report_results(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
162
|
+
self,
|
|
163
|
+
secret_id: str,
|
|
164
|
+
account_email: str,
|
|
165
|
+
report_type: str,
|
|
166
|
+
date_from: str,
|
|
167
|
+
date_to: str,
|
|
168
|
+
columns: List[str],
|
|
169
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
170
|
+
read_params: Dict[str, Union[int, str]] = None,
|
|
147
171
|
) -> List[Dict]:
|
|
148
172
|
"""
|
|
149
173
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -174,10 +198,14 @@ class SklikService:
|
|
|
174
198
|
if read_params:
|
|
175
199
|
body.update({"params": read_params})
|
|
176
200
|
|
|
177
|
-
|
|
178
|
-
|
|
201
|
+
header = get_headers(self._READ_REPORT)
|
|
202
|
+
response = self.request_session.request(
|
|
203
|
+
"post", url=self._READ_REPORT, json=body | self.session,
|
|
204
|
+
timeout=70,
|
|
205
|
+
headers=header,
|
|
179
206
|
)
|
|
180
207
|
|
|
208
|
+
|
|
181
209
|
if response.status_code == HTTPStatus.OK:
|
|
182
210
|
service_response = response.json()
|
|
183
211
|
result.extend(service_response["data"])
|
|
@@ -188,14 +216,14 @@ class SklikService:
|
|
|
188
216
|
|
|
189
217
|
@get_session_if_malformed
|
|
190
218
|
def get_streamed_report_results(
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
219
|
+
self,
|
|
220
|
+
secret_id: str,
|
|
221
|
+
account_email: str,
|
|
222
|
+
report_type: str,
|
|
223
|
+
date_from: str,
|
|
224
|
+
date_to: str,
|
|
225
|
+
columns: List[str],
|
|
226
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
199
227
|
) -> List[Dict]:
|
|
200
228
|
"""
|
|
201
229
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -224,8 +252,11 @@ class SklikService:
|
|
|
224
252
|
"columns": columns,
|
|
225
253
|
}
|
|
226
254
|
|
|
227
|
-
|
|
228
|
-
|
|
255
|
+
header = get_headers(self._READ_REPORT)
|
|
256
|
+
response = self.request_session.request(
|
|
257
|
+
"post", url=self._READ_REPORT, json=body | self.session,
|
|
258
|
+
timeout=70,
|
|
259
|
+
headers=header,
|
|
229
260
|
)
|
|
230
261
|
|
|
231
262
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -235,13 +266,13 @@ class SklikService:
|
|
|
235
266
|
raise SklikServiceException(response.content)
|
|
236
267
|
|
|
237
268
|
def _create_report(
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
269
|
+
self,
|
|
270
|
+
secret_id: str,
|
|
271
|
+
account_email: str,
|
|
272
|
+
report_type: str,
|
|
273
|
+
date_from: str,
|
|
274
|
+
date_to: str,
|
|
275
|
+
params: Dict[str, Union[int, str]] = None,
|
|
245
276
|
) -> Dict:
|
|
246
277
|
"""
|
|
247
278
|
Function creates sklik report with /create-report call
|
|
@@ -266,8 +297,11 @@ class SklikService:
|
|
|
266
297
|
}
|
|
267
298
|
body = self.params_
|
|
268
299
|
|
|
269
|
-
|
|
270
|
-
|
|
300
|
+
header = get_headers(self._CREATE_REPORT)
|
|
301
|
+
response = self.request_session.request(
|
|
302
|
+
"post", url=self._CREATE_REPORT, json=body | self.session,
|
|
303
|
+
timeout=70,
|
|
304
|
+
headers=header,
|
|
271
305
|
)
|
|
272
306
|
|
|
273
307
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -278,7 +312,7 @@ class SklikService:
|
|
|
278
312
|
|
|
279
313
|
@get_session_if_malformed
|
|
280
314
|
def call_api(
|
|
281
|
-
|
|
315
|
+
self, secret_id: str, account_email: str, method: str, params: List[Dict]
|
|
282
316
|
) -> Union[List, Dict]:
|
|
283
317
|
"""
|
|
284
318
|
Function to call SklikService get-api route
|
|
@@ -297,7 +331,13 @@ class SklikService:
|
|
|
297
331
|
"params": params,
|
|
298
332
|
}
|
|
299
333
|
|
|
300
|
-
|
|
334
|
+
header = get_headers(self._API)
|
|
335
|
+
response = self.request_session.request(
|
|
336
|
+
"post", url=self._API, json=body | self.session,
|
|
337
|
+
timeout=70,
|
|
338
|
+
headers=header,
|
|
339
|
+
)
|
|
340
|
+
|
|
301
341
|
|
|
302
342
|
if response.status_code == HTTPStatus.OK:
|
|
303
343
|
service_response = response.json()
|
|
@@ -307,9 +347,9 @@ class SklikService:
|
|
|
307
347
|
|
|
308
348
|
@get_session_if_malformed
|
|
309
349
|
def get_client(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
350
|
+
self,
|
|
351
|
+
secret_id: str,
|
|
352
|
+
account_email: str,
|
|
313
353
|
) -> Union[List, Dict]:
|
|
314
354
|
"""
|
|
315
355
|
Function to call SklikService get-client route
|
|
@@ -321,12 +361,16 @@ class SklikService:
|
|
|
321
361
|
if self.session is None:
|
|
322
362
|
self.get_session(secret_id, account_email)
|
|
323
363
|
|
|
324
|
-
|
|
364
|
+
header = get_headers(self._GET_CLIENT)
|
|
365
|
+
response = self.request_session.request(
|
|
325
366
|
"post",
|
|
326
367
|
url=self._GET_CLIENT,
|
|
327
368
|
json={"sklik_session": self.session["sklik_session"]},
|
|
369
|
+
timeout=70,
|
|
370
|
+
headers=header,
|
|
328
371
|
)
|
|
329
372
|
|
|
373
|
+
|
|
330
374
|
if response.status_code == HTTPStatus.OK:
|
|
331
375
|
service_response = response.json()
|
|
332
376
|
return service_response["data"]
|
|
@@ -335,8 +379,8 @@ class SklikService:
|
|
|
335
379
|
|
|
336
380
|
@get_session_if_malformed
|
|
337
381
|
def get_accessible_accounts(
|
|
338
|
-
|
|
339
|
-
|
|
382
|
+
self,
|
|
383
|
+
secret_id: str,
|
|
340
384
|
) -> Union[List, Dict]:
|
|
341
385
|
"""
|
|
342
386
|
Function to retrieve accessible accounts
|
|
@@ -347,37 +391,41 @@ class SklikService:
|
|
|
347
391
|
if self.session is None:
|
|
348
392
|
self.get_session_without_account(secret_id)
|
|
349
393
|
|
|
350
|
-
|
|
394
|
+
header = get_headers(self._GET_CLIENT)
|
|
395
|
+
response = self.request_session.request(
|
|
351
396
|
"post",
|
|
352
397
|
url=self._GET_CLIENT,
|
|
353
398
|
json={
|
|
354
399
|
"sklik_session": self.session["sklik_session"],
|
|
355
|
-
"filters": {
|
|
356
|
-
"hide_inactive": True
|
|
357
|
-
}
|
|
400
|
+
"filters": {"hide_inactive": True},
|
|
358
401
|
},
|
|
402
|
+
timeout=70,
|
|
403
|
+
headers=header,
|
|
359
404
|
)
|
|
360
405
|
|
|
406
|
+
|
|
361
407
|
if response.status_code == HTTPStatus.OK:
|
|
362
408
|
accounts = []
|
|
363
409
|
service_response = response.json()
|
|
364
410
|
data = service_response["data"]
|
|
365
411
|
for account in data["foreignAccounts"]:
|
|
366
|
-
accounts.append(
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
412
|
+
accounts.append(
|
|
413
|
+
{
|
|
414
|
+
"id": account["userId"],
|
|
415
|
+
"name": account["username"],
|
|
416
|
+
"active": account["relationStatus"] == "live",
|
|
417
|
+
}
|
|
418
|
+
)
|
|
371
419
|
return accounts
|
|
372
420
|
else:
|
|
373
421
|
raise SklikServiceException(response.content)
|
|
374
422
|
|
|
375
423
|
@get_session_if_malformed
|
|
376
424
|
def check_data_ready(
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
425
|
+
self,
|
|
426
|
+
secret_id: str,
|
|
427
|
+
account_email: str,
|
|
428
|
+
date: str = None,
|
|
381
429
|
) -> int:
|
|
382
430
|
"""
|
|
383
431
|
Checks if data on server are ready
|
|
@@ -391,15 +439,19 @@ class SklikService:
|
|
|
391
439
|
self.get_session(secret_id, account_email)
|
|
392
440
|
|
|
393
441
|
date = date or (
|
|
394
|
-
|
|
395
|
-
|
|
442
|
+
datetime.now(timezone("UTC")).astimezone(timezone("Europe/Prague"))
|
|
443
|
+
- timedelta(days=1)
|
|
396
444
|
).strftime("%Y-%m-%d")
|
|
397
445
|
|
|
398
446
|
body = {"date": date}
|
|
399
447
|
|
|
400
|
-
|
|
401
|
-
|
|
448
|
+
header = get_headers(self._CHECK_DATA_READY)
|
|
449
|
+
response = self.request_session.request(
|
|
450
|
+
"post", url=self._CHECK_DATA_READY, json=body | self.session,
|
|
451
|
+
timeout=70,
|
|
452
|
+
headers=header,
|
|
402
453
|
)
|
|
454
|
+
|
|
403
455
|
if response.status_code == HTTPStatus.OK:
|
|
404
456
|
service_response = response.json()
|
|
405
457
|
if service_response["data"] == DataStatus.PENDING:
|
|
@@ -413,9 +465,9 @@ class SklikService:
|
|
|
413
465
|
|
|
414
466
|
@get_session_if_malformed
|
|
415
467
|
def fetch_api_limits(
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
468
|
+
self,
|
|
469
|
+
secret_id: str,
|
|
470
|
+
account_email: str,
|
|
419
471
|
) -> Union[List, Dict]:
|
|
420
472
|
"""
|
|
421
473
|
Function to call SklikService api/limits route
|
|
@@ -427,7 +479,12 @@ class SklikService:
|
|
|
427
479
|
if self.session is None:
|
|
428
480
|
self.get_session(secret_id, account_email)
|
|
429
481
|
|
|
430
|
-
|
|
482
|
+
header = get_headers(self._API_LIMITS)
|
|
483
|
+
response = self.request_session.request(
|
|
484
|
+
"post", url=self._API_LIMITS, json=self.session,
|
|
485
|
+
timeout=70,
|
|
486
|
+
headers=header,
|
|
487
|
+
)
|
|
431
488
|
|
|
432
489
|
if response.status_code == HTTPStatus.OK:
|
|
433
490
|
service_response = response.json()
|
|
@@ -443,9 +500,19 @@ class SklikService:
|
|
|
443
500
|
:return: True if account has access
|
|
444
501
|
"""
|
|
445
502
|
body = {"account_email": account_email, "secret_id": secret_id}
|
|
446
|
-
|
|
503
|
+
header = get_headers(self._GET_SESSION)
|
|
504
|
+
response = self.request_session.request(
|
|
505
|
+
"post", url=self._GET_SESSION, json=body,
|
|
506
|
+
timeout=70,
|
|
507
|
+
headers=header,
|
|
508
|
+
)
|
|
509
|
+
|
|
447
510
|
if response.status_code == 200:
|
|
448
511
|
return True
|
|
449
|
-
if
|
|
512
|
+
if (
|
|
513
|
+
response.status_code == 401
|
|
514
|
+
or response.status_code == 403
|
|
515
|
+
or response.status_code == 404
|
|
516
|
+
):
|
|
450
517
|
return False
|
|
451
518
|
raise SklikServiceException(response.content)
|
|
@@ -15,21 +15,19 @@ def get_headers(route):
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def get_retry_session():
|
|
19
|
+
session = requests.Session()
|
|
20
|
+
|
|
19
21
|
retry_strategy = Retry(
|
|
20
22
|
total=3,
|
|
21
23
|
raise_on_status=False,
|
|
22
|
-
status_forcelist=[429, 500, 502, 503, 504],
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
status_forcelist=[104,400,429, 500, 502, 503, 504],
|
|
25
|
+
allowed_methods=None,
|
|
26
|
+
backoff_factor=1
|
|
25
27
|
)
|
|
28
|
+
|
|
26
29
|
adapter = HTTPAdapter(max_retries=retry_strategy)
|
|
27
|
-
http
|
|
28
|
-
|
|
29
|
-
http.mount("http://", adapter)
|
|
30
|
-
header = get_headers(url)
|
|
31
|
-
# 70 is magic number, we tolerate the API to tell us to wait 60 seconds max,
|
|
32
|
-
# otherwise we kill it
|
|
33
|
-
response = http.request(method, url=url, json=json, timeout=70, headers=header)
|
|
30
|
+
session.mount("http://", adapter)
|
|
31
|
+
session.mount("https://", adapter)
|
|
34
32
|
|
|
35
|
-
return
|
|
33
|
+
return session
|
|
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
|