logos-sdk 0.0.25.dev10__tar.gz → 0.0.25.dev12__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.dev10 → logos_sdk-0.0.25.dev12}/PKG-INFO +10 -2
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/CampaignManager.py +70 -45
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/DV360.py +62 -39
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Facebook.py +35 -13
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/GoogleAds.py +50 -28
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MerchantCenter.py +37 -12
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MicrosoftAdvertising.py +30 -7
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Sklik.py +148 -66
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/__init__.py +10 -12
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/PKG-INFO +10 -2
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/LICENSE +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/README.md +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/__init__.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/big_query/BigQuery.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/big_query/__init__.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/logging/LogosLogger.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/logging/__init__.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Collabim.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/GoogleSheets.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MarketMiner.py +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/SOURCES.txt +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/dependency_links.txt +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/requires.txt +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/top_level.txt +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/setup.cfg +0 -0
- {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: logos-sdk
|
|
3
|
-
Version: 0.0.25.
|
|
3
|
+
Version: 0.0.25.dev12
|
|
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
|
|
@@ -19,6 +19,14 @@ Requires-Dist: httplib2
|
|
|
19
19
|
Requires-Dist: pandas
|
|
20
20
|
Requires-Dist: db-dtypes
|
|
21
21
|
Requires-Dist: numpy
|
|
22
|
+
Dynamic: author
|
|
23
|
+
Dynamic: author-email
|
|
24
|
+
Dynamic: classifier
|
|
25
|
+
Dynamic: description
|
|
26
|
+
Dynamic: description-content-type
|
|
27
|
+
Dynamic: home-page
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: summary
|
|
22
30
|
|
|
23
31
|
# Logos Software Development Kit
|
|
24
32
|
|
|
@@ -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"
|
|
@@ -32,17 +32,18 @@ class CampaignManagerService:
|
|
|
32
32
|
self._QUERY_DIMENSION_VALUES = self._URL + "/query-dimension-values"
|
|
33
33
|
self._GET_ACCESSIBLE_PARENT_ACCOUNTS = self._URL + "/get-parent-accounts"
|
|
34
34
|
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-advertisers"
|
|
35
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
35
36
|
|
|
36
37
|
def create_report(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
self,
|
|
39
|
+
account_id: str,
|
|
40
|
+
name: str,
|
|
41
|
+
start_date: str,
|
|
42
|
+
end_date: str,
|
|
43
|
+
dimensions: list,
|
|
44
|
+
metrics_names: list,
|
|
45
|
+
dimension_filters: list,
|
|
46
|
+
secret_id: str,
|
|
46
47
|
) -> Dict:
|
|
47
48
|
"""
|
|
48
49
|
Method for creating a report in API and returning a dict containing its ID for further use
|
|
@@ -66,7 +67,7 @@ class CampaignManagerService:
|
|
|
66
67
|
"secret_id": secret_id,
|
|
67
68
|
}
|
|
68
69
|
header = get_headers(self._CREATE_REPORT)
|
|
69
|
-
response = request(
|
|
70
|
+
response = self.session.request(
|
|
70
71
|
method="post", url=self._CREATE_REPORT, json=body, headers=header
|
|
71
72
|
)
|
|
72
73
|
|
|
@@ -91,7 +92,7 @@ class CampaignManagerService:
|
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
header = get_headers(self._GET_REPORT)
|
|
94
|
-
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)
|
|
95
96
|
|
|
96
97
|
if response.status_code == HTTPStatus.OK:
|
|
97
98
|
service_response = response.json()
|
|
@@ -113,7 +114,7 @@ class CampaignManagerService:
|
|
|
113
114
|
"secret_id": secret_id,
|
|
114
115
|
}
|
|
115
116
|
header = get_headers(self._RUN_REPORT)
|
|
116
|
-
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)
|
|
117
118
|
service_response = response.json()
|
|
118
119
|
|
|
119
120
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -172,7 +173,7 @@ class CampaignManagerService:
|
|
|
172
173
|
"""
|
|
173
174
|
body = {"report_id": report_id, "file_id": file_id, "secret_id": secret_id}
|
|
174
175
|
header = get_headers(self._GET_FILE)
|
|
175
|
-
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)
|
|
176
177
|
|
|
177
178
|
if response.status_code == HTTPStatus.OK:
|
|
178
179
|
service_response = response.json()
|
|
@@ -181,7 +182,7 @@ class CampaignManagerService:
|
|
|
181
182
|
raise CampaignManagerServiceException(response.content)
|
|
182
183
|
|
|
183
184
|
def check_report_ready_with_exponential_backoff(
|
|
184
|
-
|
|
185
|
+
self, report_id: str, file_id: str, secret_id: str, backoff_attempts: int = 10
|
|
185
186
|
) -> bool:
|
|
186
187
|
"""
|
|
187
188
|
Implements exponential backoff for pooling the API for readiness of the report, suggested in
|
|
@@ -196,7 +197,7 @@ class CampaignManagerService:
|
|
|
196
197
|
if self.check_report_ready(report_id, file_id, secret_id):
|
|
197
198
|
return True
|
|
198
199
|
else:
|
|
199
|
-
time.sleep((2**attempt) + randint(1, 20))
|
|
200
|
+
time.sleep((2 ** attempt) + randint(1, 20))
|
|
200
201
|
|
|
201
202
|
return False
|
|
202
203
|
|
|
@@ -214,7 +215,7 @@ class CampaignManagerService:
|
|
|
214
215
|
header = get_headers(self._GET_FILE_MEDIA_REQUEST)
|
|
215
216
|
|
|
216
217
|
# fetch the authorized request from our service for downloading the report from API
|
|
217
|
-
response = request(
|
|
218
|
+
response = self.session.request(
|
|
218
219
|
"post", url=self._GET_FILE_MEDIA_REQUEST, json=body, headers=header
|
|
219
220
|
)
|
|
220
221
|
|
|
@@ -282,7 +283,7 @@ class CampaignManagerService:
|
|
|
282
283
|
"secret_id": secret_id,
|
|
283
284
|
}
|
|
284
285
|
header = get_headers(self._DELETE_REPORT)
|
|
285
|
-
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)
|
|
286
287
|
|
|
287
288
|
if response.status_code == HTTPStatus.OK:
|
|
288
289
|
return True
|
|
@@ -290,14 +291,14 @@ class CampaignManagerService:
|
|
|
290
291
|
raise CampaignManagerServiceException(response.content)
|
|
291
292
|
|
|
292
293
|
def query_dimension_values(
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
294
|
+
self,
|
|
295
|
+
account_id: str,
|
|
296
|
+
secret_id: str,
|
|
297
|
+
dimension_name: str,
|
|
298
|
+
filters: List,
|
|
299
|
+
start_date: str,
|
|
300
|
+
end_date: int,
|
|
301
|
+
max_results: int = 100,
|
|
301
302
|
) -> List[Dict]:
|
|
302
303
|
"""
|
|
303
304
|
Retrieves values of selected dimensions for data filtered according to rules set in list of filters
|
|
@@ -324,7 +325,7 @@ class CampaignManagerService:
|
|
|
324
325
|
}
|
|
325
326
|
|
|
326
327
|
header = get_headers(self._QUERY_DIMENSION_VALUES)
|
|
327
|
-
response = request(
|
|
328
|
+
response = self.session.request(
|
|
328
329
|
"post", url=self._QUERY_DIMENSION_VALUES, json=body, headers=header
|
|
329
330
|
)
|
|
330
331
|
|
|
@@ -337,7 +338,7 @@ class CampaignManagerService:
|
|
|
337
338
|
# if there was a last page response is empty string
|
|
338
339
|
while service_response["data"]["nextPageToken"]:
|
|
339
340
|
body["page_token"] = service_response["data"]["nextPageToken"]
|
|
340
|
-
response = request(
|
|
341
|
+
response = self.session.request(
|
|
341
342
|
"post", url=self._QUERY_DIMENSION_VALUES, json=body, headers=header
|
|
342
343
|
)
|
|
343
344
|
|
|
@@ -348,16 +349,16 @@ class CampaignManagerService:
|
|
|
348
349
|
yield service_response["data"]["items"]
|
|
349
350
|
|
|
350
351
|
def create_and_get_report_results(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
352
|
+
self,
|
|
353
|
+
account_id: str,
|
|
354
|
+
name: str,
|
|
355
|
+
start_date: str,
|
|
356
|
+
end_date: str,
|
|
357
|
+
dimensions: list,
|
|
358
|
+
metrics_names: list,
|
|
359
|
+
dimension_filters: list,
|
|
360
|
+
secret_id: str,
|
|
361
|
+
backoff_attempts: int,
|
|
361
362
|
) -> Dict:
|
|
362
363
|
"""
|
|
363
364
|
Method to create report, run it and fetch results in one go
|
|
@@ -380,10 +381,10 @@ class CampaignManagerService:
|
|
|
380
381
|
secret_id=secret_id,
|
|
381
382
|
)
|
|
382
383
|
if self.check_report_ready_with_exponential_backoff(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
384
|
+
report_id=report["id"],
|
|
385
|
+
file_id=run_report["id"],
|
|
386
|
+
secret_id=secret_id,
|
|
387
|
+
backoff_attempts=backoff_attempts,
|
|
387
388
|
):
|
|
388
389
|
return self.get_report_results(
|
|
389
390
|
report_id=report["id"],
|
|
@@ -410,7 +411,7 @@ class CampaignManagerService:
|
|
|
410
411
|
header = get_headers(self._GET_ACCESSIBLE_PARENT_ACCOUNTS)
|
|
411
412
|
body = {"secret_id": secret_id}
|
|
412
413
|
|
|
413
|
-
response = request(
|
|
414
|
+
response = self.session.request(
|
|
414
415
|
"post", url=self._GET_ACCESSIBLE_PARENT_ACCOUNTS, json=body, headers=header
|
|
415
416
|
)
|
|
416
417
|
|
|
@@ -435,7 +436,7 @@ class CampaignManagerService:
|
|
|
435
436
|
},
|
|
436
437
|
}
|
|
437
438
|
|
|
438
|
-
response = request(
|
|
439
|
+
response = self.session.request(
|
|
439
440
|
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
440
441
|
)
|
|
441
442
|
|
|
@@ -444,3 +445,27 @@ class CampaignManagerService:
|
|
|
444
445
|
return service_response["data"]
|
|
445
446
|
else:
|
|
446
447
|
raise CampaignManagerServiceException(response.content)
|
|
448
|
+
|
|
449
|
+
def get_account_accessibility(self, secret_id: str,account_id, advertiser_id: str) -> bool:
|
|
450
|
+
"""
|
|
451
|
+
Gets account accessibility
|
|
452
|
+
:param secret_id: The ID of the secret in secret manager
|
|
453
|
+
:param account_id: Account ID
|
|
454
|
+
:param advertiser_id: The ID of the advertiser.
|
|
455
|
+
:return: True if account has access
|
|
456
|
+
"""
|
|
457
|
+
body = {
|
|
458
|
+
"secret_id": secret_id,
|
|
459
|
+
"account_id": account_id,
|
|
460
|
+
"advertiser_id": advertiser_id
|
|
461
|
+
}
|
|
462
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
463
|
+
response = self.session.request(
|
|
464
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
if response.status_code == HTTPStatus.OK:
|
|
468
|
+
service_response = response.json()
|
|
469
|
+
return service_response["data"]
|
|
470
|
+
else:
|
|
471
|
+
raise CampaignManagerServiceException(response.content)
|
|
@@ -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,13 +16,14 @@ 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 = (
|
|
23
|
-
|
|
23
|
+
self._URL + "/bulk-list-line-item-assigned-targeting-options"
|
|
24
24
|
)
|
|
25
25
|
self._BULK_EDIT_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS = (
|
|
26
|
-
|
|
26
|
+
self._URL + "/bulk-edit-line-item-assigned-targeting-options"
|
|
27
27
|
)
|
|
28
28
|
self._CREATE_CHANNEL = self._URL + "/create-channel"
|
|
29
29
|
self._LIST_CHANNELS = self._URL + "/list-channels"
|
|
@@ -36,6 +36,7 @@ class DV360Service:
|
|
|
36
36
|
self._DELETE_QUERY = self._URL + "/delete-query"
|
|
37
37
|
self._GET_ACCESSIBLE_PARTNERS = self._URL + "/get-accessible-partners"
|
|
38
38
|
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-accessible-advertisers"
|
|
39
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
39
40
|
|
|
40
41
|
def list_line_items(self, advertiser_id, secret_id, filter_string=None):
|
|
41
42
|
"""
|
|
@@ -51,7 +52,7 @@ class DV360Service:
|
|
|
51
52
|
if filter_string is not None:
|
|
52
53
|
body["filter"] = filter_string
|
|
53
54
|
|
|
54
|
-
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)
|
|
55
56
|
|
|
56
57
|
if response.status_code == HTTPStatus.OK:
|
|
57
58
|
service_response = response.json()
|
|
@@ -60,11 +61,11 @@ class DV360Service:
|
|
|
60
61
|
raise DV360ServiceException(response.content)
|
|
61
62
|
|
|
62
63
|
def bulk_list_line_item_assigned_targeting_options(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
self,
|
|
65
|
+
advertiser_id,
|
|
66
|
+
secret_id,
|
|
67
|
+
line_item_ids,
|
|
68
|
+
filter_string,
|
|
68
69
|
):
|
|
69
70
|
"""
|
|
70
71
|
Bulk lists targeting options under multiple line items
|
|
@@ -74,7 +75,7 @@ class DV360Service:
|
|
|
74
75
|
:param filter_string: Allows filtering by line item fields
|
|
75
76
|
:return List of AssignedTargetingOption objects
|
|
76
77
|
"""
|
|
77
|
-
header = get_headers(self.
|
|
78
|
+
header = get_headers(self._BULK_LIST_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS)
|
|
78
79
|
body = {
|
|
79
80
|
"advertiser_id": advertiser_id,
|
|
80
81
|
"secret_id": secret_id,
|
|
@@ -82,7 +83,7 @@ class DV360Service:
|
|
|
82
83
|
"filter": filter_string,
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
response = request(
|
|
86
|
+
response = self.session.request(
|
|
86
87
|
"post",
|
|
87
88
|
url=self._BULK_LIST_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS,
|
|
88
89
|
json=body,
|
|
@@ -96,12 +97,12 @@ class DV360Service:
|
|
|
96
97
|
raise DV360ServiceException(response.content)
|
|
97
98
|
|
|
98
99
|
def bulk_edit_line_item_assigned_targeting_options(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
self,
|
|
101
|
+
advertiser_id,
|
|
102
|
+
secret_id,
|
|
103
|
+
line_item_ids,
|
|
104
|
+
delete_requests=None,
|
|
105
|
+
create_requests=None,
|
|
105
106
|
):
|
|
106
107
|
"""
|
|
107
108
|
Bulk edits targeting options under multiple line items
|
|
@@ -127,7 +128,7 @@ class DV360Service:
|
|
|
127
128
|
if create_requests is not None:
|
|
128
129
|
body["create_requests"] = create_requests
|
|
129
130
|
|
|
130
|
-
response = request(
|
|
131
|
+
response = self.session.request(
|
|
131
132
|
"post",
|
|
132
133
|
url=self._BULK_EDIT_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS,
|
|
133
134
|
json=body,
|
|
@@ -151,7 +152,7 @@ class DV360Service:
|
|
|
151
152
|
header = get_headers(self._CREATE_CHANNEL)
|
|
152
153
|
body = {"advertiser_id": advertiser_id, "secret_id": secret_id, "name": name}
|
|
153
154
|
|
|
154
|
-
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)
|
|
155
156
|
|
|
156
157
|
if response.status_code == HTTPStatus.OK:
|
|
157
158
|
service_response = response.json()
|
|
@@ -173,7 +174,7 @@ class DV360Service:
|
|
|
173
174
|
if filter_string is not None:
|
|
174
175
|
body["filter"] = filter_string
|
|
175
176
|
|
|
176
|
-
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)
|
|
177
178
|
|
|
178
179
|
if response.status_code == HTTPStatus.OK:
|
|
179
180
|
service_response = response.json()
|
|
@@ -182,7 +183,7 @@ class DV360Service:
|
|
|
182
183
|
raise DV360ServiceException(response.content)
|
|
183
184
|
|
|
184
185
|
def list_channel_sites(
|
|
185
|
-
|
|
186
|
+
self, advertiser_id, secret_id, channel_id, filter_string=None
|
|
186
187
|
):
|
|
187
188
|
"""
|
|
188
189
|
Lists channels for advertiser
|
|
@@ -202,7 +203,7 @@ class DV360Service:
|
|
|
202
203
|
if filter_string is not None:
|
|
203
204
|
body["filter"] = filter_string
|
|
204
205
|
|
|
205
|
-
response = request(
|
|
206
|
+
response = self.session.request(
|
|
206
207
|
"post", url=self._LIST_CHANNEL_SITES, json=body, headers=header
|
|
207
208
|
)
|
|
208
209
|
|
|
@@ -213,12 +214,12 @@ class DV360Service:
|
|
|
213
214
|
raise DV360ServiceException(response.content)
|
|
214
215
|
|
|
215
216
|
def bulk_edit_channels_sites(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
217
|
+
self,
|
|
218
|
+
advertiser_id,
|
|
219
|
+
secret_id,
|
|
220
|
+
channel_id,
|
|
221
|
+
deleted_sites=None,
|
|
222
|
+
created_sites=None,
|
|
222
223
|
):
|
|
223
224
|
"""
|
|
224
225
|
Bulk edits sites under a single channel
|
|
@@ -242,7 +243,7 @@ class DV360Service:
|
|
|
242
243
|
if created_sites is not None:
|
|
243
244
|
body["created_sites"] = created_sites
|
|
244
245
|
|
|
245
|
-
response = request(
|
|
246
|
+
response = self.session.request(
|
|
246
247
|
"post",
|
|
247
248
|
url=self._BULK_EDIT_CHANNEL_SITES,
|
|
248
249
|
json=body,
|
|
@@ -276,7 +277,7 @@ class DV360Service:
|
|
|
276
277
|
"metrics_names": metrics_names,
|
|
277
278
|
}
|
|
278
279
|
|
|
279
|
-
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)
|
|
280
281
|
|
|
281
282
|
if response.status_code == HTTPStatus.OK:
|
|
282
283
|
service_response = response.json()
|
|
@@ -295,7 +296,7 @@ class DV360Service:
|
|
|
295
296
|
header = get_headers(self._RUN_QUERY)
|
|
296
297
|
body = {"secret_id": secret_id, "query_id": query_id}
|
|
297
298
|
|
|
298
|
-
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)
|
|
299
300
|
|
|
300
301
|
if response.status_code == HTTPStatus.OK:
|
|
301
302
|
service_response = response.json()
|
|
@@ -318,7 +319,7 @@ class DV360Service:
|
|
|
318
319
|
},
|
|
319
320
|
}
|
|
320
321
|
|
|
321
|
-
response = request(
|
|
322
|
+
response = self.session.request(
|
|
322
323
|
"post", url=self._GET_ACCESSIBLE_PARTNERS, json=body, headers=header
|
|
323
324
|
)
|
|
324
325
|
|
|
@@ -343,7 +344,7 @@ class DV360Service:
|
|
|
343
344
|
},
|
|
344
345
|
}
|
|
345
346
|
|
|
346
|
-
response = request(
|
|
347
|
+
response = self.session.request(
|
|
347
348
|
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
348
349
|
)
|
|
349
350
|
|
|
@@ -353,8 +354,30 @@ class DV360Service:
|
|
|
353
354
|
else:
|
|
354
355
|
raise DV360ServiceException(response.content)
|
|
355
356
|
|
|
357
|
+
def get_account_accessibility(self, secret_id: str, advertiser_id: str) -> bool:
|
|
358
|
+
"""
|
|
359
|
+
Gets account accessibility
|
|
360
|
+
:param secret_id: The ID of the secret in secret manager
|
|
361
|
+
:param advertiser_id: The ID of the advertiser.
|
|
362
|
+
:return: True if account has access
|
|
363
|
+
"""
|
|
364
|
+
body = {
|
|
365
|
+
"secret_id": secret_id,
|
|
366
|
+
"advertiser_id": advertiser_id
|
|
367
|
+
}
|
|
368
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
369
|
+
response = self.session.request(
|
|
370
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if response.status_code == HTTPStatus.OK:
|
|
374
|
+
service_response = response.json()
|
|
375
|
+
return service_response["data"]
|
|
376
|
+
else:
|
|
377
|
+
raise DV360ServiceException(response.content)
|
|
378
|
+
|
|
356
379
|
def _check_report_ready_with_exponential_backoff(
|
|
357
|
-
|
|
380
|
+
self, secret_id: str, query_id: str, report_id: str, backoff_attempts: int = 10
|
|
358
381
|
) -> bool:
|
|
359
382
|
"""
|
|
360
383
|
Implements exponential backoff for pooling the API for readiness of the report, suggested in
|
|
@@ -370,7 +393,7 @@ class DV360Service:
|
|
|
370
393
|
elif state == "FAILED":
|
|
371
394
|
raise DV360ServiceException("Report failed to generate")
|
|
372
395
|
else:
|
|
373
|
-
sleep((2**attempt) + randint(1, 20))
|
|
396
|
+
sleep((2 ** attempt) + randint(1, 20))
|
|
374
397
|
|
|
375
398
|
return False
|
|
376
399
|
|
|
@@ -385,7 +408,7 @@ class DV360Service:
|
|
|
385
408
|
header = get_headers(self._GET_QUERY_METADATA)
|
|
386
409
|
body = {"secret_id": secret_id, "query_id": query_id, "report_id": report_id}
|
|
387
410
|
|
|
388
|
-
response = request(
|
|
411
|
+
response = self.session.request(
|
|
389
412
|
"post", url=self._GET_QUERY_METADATA, json=body, headers=header
|
|
390
413
|
)
|
|
391
414
|
|
|
@@ -404,7 +427,7 @@ class DV360Service:
|
|
|
404
427
|
:return: The results
|
|
405
428
|
"""
|
|
406
429
|
if not self._check_report_ready_with_exponential_backoff(
|
|
407
|
-
|
|
430
|
+
secret_id, query_id, report_id
|
|
408
431
|
):
|
|
409
432
|
raise DV360ServiceException("Report did not generate in time")
|
|
410
433
|
|
|
@@ -441,7 +464,7 @@ class DV360Service:
|
|
|
441
464
|
header = get_headers(self._DELETE_QUERY)
|
|
442
465
|
body = {"secret_id": secret_id, "query_id": query_id}
|
|
443
466
|
|
|
444
|
-
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)
|
|
445
468
|
|
|
446
469
|
if response.status_code == HTTPStatus.OK:
|
|
447
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"
|
|
@@ -27,6 +27,7 @@ class FacebookService:
|
|
|
27
27
|
self._FEED_ERRORS = self._URL + "/feed-errors"
|
|
28
28
|
self._FEED_ERRORS_REPORT_STATUS = self._URL + "/feed-errors-report-status"
|
|
29
29
|
self._FEED_ERRORS_REPORT = self._URL + "/feed-errors-report"
|
|
30
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
30
31
|
|
|
31
32
|
def get_accessible_accounts(
|
|
32
33
|
self, secret_id: str, timeout: int = None
|
|
@@ -47,7 +48,7 @@ class FacebookService:
|
|
|
47
48
|
|
|
48
49
|
header = get_headers(self._ACCESSIBLE_ACCOUNTS)
|
|
49
50
|
|
|
50
|
-
response = request(
|
|
51
|
+
response = self.session.request(
|
|
51
52
|
"post",
|
|
52
53
|
url=self._ACCESSIBLE_ACCOUNTS,
|
|
53
54
|
json=body,
|
|
@@ -61,6 +62,27 @@ class FacebookService:
|
|
|
61
62
|
else:
|
|
62
63
|
raise FacebookServiceException(response.content)
|
|
63
64
|
|
|
65
|
+
def get_account_accessibility(self, secret_id: str, account_id: str) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Gets account accessibility
|
|
68
|
+
:param secret_id: The ID of the secret in secret manager
|
|
69
|
+
:param account_id: The ID of the account.
|
|
70
|
+
:return: True if account has access
|
|
71
|
+
"""
|
|
72
|
+
body = {
|
|
73
|
+
"secret_id": secret_id,
|
|
74
|
+
"account_id": account_id
|
|
75
|
+
}
|
|
76
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
77
|
+
response = self.session.request(
|
|
78
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if response.status_code == HTTPStatus.OK:
|
|
82
|
+
service_response = response.json()
|
|
83
|
+
return service_response["data"]
|
|
84
|
+
else:
|
|
85
|
+
raise FacebookServiceException(response.content)
|
|
64
86
|
def get_insights(
|
|
65
87
|
self,
|
|
66
88
|
account_id: str,
|
|
@@ -99,7 +121,7 @@ class FacebookService:
|
|
|
99
121
|
body["level"] = level
|
|
100
122
|
header = get_headers(self._INSIGHTS)
|
|
101
123
|
|
|
102
|
-
response = request(
|
|
124
|
+
response = self.session.request(
|
|
103
125
|
"post", url=self._INSIGHTS, json=body, headers=header, timeout=timeout
|
|
104
126
|
)
|
|
105
127
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -140,7 +162,7 @@ class FacebookService:
|
|
|
140
162
|
|
|
141
163
|
header = get_headers(self._LINK_URLS)
|
|
142
164
|
|
|
143
|
-
response = request(
|
|
165
|
+
response = self.session.request(
|
|
144
166
|
"post", url=self._LINK_URLS, json=body, headers=header, timeout=timeout
|
|
145
167
|
)
|
|
146
168
|
|
|
@@ -165,7 +187,7 @@ class FacebookService:
|
|
|
165
187
|
|
|
166
188
|
header = get_headers(self._ACCESSIBLE_BUSINESSES)
|
|
167
189
|
|
|
168
|
-
response = request(
|
|
190
|
+
response = self.session.request(
|
|
169
191
|
"post",
|
|
170
192
|
url=self._ACCESSIBLE_BUSINESSES,
|
|
171
193
|
json=body,
|
|
@@ -204,7 +226,7 @@ class FacebookService:
|
|
|
204
226
|
|
|
205
227
|
header = get_headers(self._PRODUCT_CATALOGS)
|
|
206
228
|
|
|
207
|
-
response = request(
|
|
229
|
+
response = self.session.request(
|
|
208
230
|
"post",
|
|
209
231
|
url=self._PRODUCT_CATALOGS,
|
|
210
232
|
json=body,
|
|
@@ -240,7 +262,7 @@ class FacebookService:
|
|
|
240
262
|
|
|
241
263
|
header = get_headers(self._PRODUCT_CATALOG)
|
|
242
264
|
|
|
243
|
-
response = request(
|
|
265
|
+
response = self.session.request(
|
|
244
266
|
"post",
|
|
245
267
|
url=self._PRODUCT_CATALOG,
|
|
246
268
|
json=body,
|
|
@@ -282,7 +304,7 @@ class FacebookService:
|
|
|
282
304
|
|
|
283
305
|
header = get_headers(self._PRODUCTS)
|
|
284
306
|
|
|
285
|
-
response = request(
|
|
307
|
+
response = self.session.request(
|
|
286
308
|
"post", url=self._PRODUCTS, json=body, headers=header, timeout=timeout
|
|
287
309
|
)
|
|
288
310
|
if response.status_code == HTTPStatus.OK:
|
|
@@ -303,7 +325,7 @@ class FacebookService:
|
|
|
303
325
|
body = {"secret_id": secret_id, "catalog_id": catalog_id}
|
|
304
326
|
|
|
305
327
|
header = get_headers(self._FEEDS)
|
|
306
|
-
response = request(
|
|
328
|
+
response = self.session.request(
|
|
307
329
|
"post", url=self._FEEDS, json=body, headers=header, timeout=timeout
|
|
308
330
|
)
|
|
309
331
|
|
|
@@ -326,7 +348,7 @@ class FacebookService:
|
|
|
326
348
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
327
349
|
|
|
328
350
|
header = get_headers(self._FEED_ERRORS)
|
|
329
|
-
response = request(
|
|
351
|
+
response = self.session.request(
|
|
330
352
|
"post", url=self._FEED_ERRORS, json=body, headers=header, timeout=timeout
|
|
331
353
|
)
|
|
332
354
|
|
|
@@ -348,7 +370,7 @@ class FacebookService:
|
|
|
348
370
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
349
371
|
|
|
350
372
|
header = get_headers(self._FEED_ERRORS_REPORT_STATUS)
|
|
351
|
-
response = request(
|
|
373
|
+
response = self.session.request(
|
|
352
374
|
"post",
|
|
353
375
|
url=self._FEED_ERRORS_REPORT_STATUS,
|
|
354
376
|
json=body,
|
|
@@ -376,7 +398,7 @@ class FacebookService:
|
|
|
376
398
|
body = {"secret_id": secret_id, "feed_id": feed_id}
|
|
377
399
|
|
|
378
400
|
header = get_headers(self._FEED_ERRORS_REPORT)
|
|
379
|
-
response = request(
|
|
401
|
+
response = self.session.request(
|
|
380
402
|
"post",
|
|
381
403
|
url=self._FEED_ERRORS_REPORT,
|
|
382
404
|
json=body,
|