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.
Files changed (26) hide show
  1. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/PKG-INFO +10 -2
  2. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/CampaignManager.py +70 -45
  3. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/DV360.py +62 -39
  4. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Facebook.py +35 -13
  5. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/GoogleAds.py +50 -28
  6. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MerchantCenter.py +37 -12
  7. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MicrosoftAdvertising.py +30 -7
  8. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Sklik.py +148 -66
  9. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/__init__.py +10 -12
  10. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/PKG-INFO +10 -2
  11. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/LICENSE +0 -0
  12. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/README.md +0 -0
  13. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/__init__.py +0 -0
  14. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/big_query/BigQuery.py +0 -0
  15. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/big_query/__init__.py +0 -0
  16. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/logging/LogosLogger.py +0 -0
  17. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/logging/__init__.py +0 -0
  18. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/Collabim.py +0 -0
  19. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/GoogleSheets.py +0 -0
  20. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk/services/MarketMiner.py +0 -0
  21. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/SOURCES.txt +0 -0
  22. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/dependency_links.txt +0 -0
  23. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/requires.txt +0 -0
  24. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/logos_sdk.egg-info/top_level.txt +0 -0
  25. {logos_sdk-0.0.25.dev10 → logos_sdk-0.0.25.dev12}/setup.cfg +0 -0
  26. {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
1
+ Metadata-Version: 2.2
2
2
  Name: logos-sdk
3
- Version: 0.0.25.dev10
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
- self,
38
- account_id: str,
39
- name: str,
40
- start_date: str,
41
- end_date: str,
42
- dimensions: list,
43
- metrics_names: list,
44
- dimension_filters: list,
45
- secret_id: str,
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
- self, report_id: str, file_id: str, secret_id: str, backoff_attempts: int = 10
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
- self,
294
- account_id: str,
295
- secret_id: str,
296
- dimension_name: str,
297
- filters: List,
298
- start_date: str,
299
- end_date: int,
300
- max_results: int = 100,
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
- self,
352
- account_id: str,
353
- name: str,
354
- start_date: str,
355
- end_date: str,
356
- dimensions: list,
357
- metrics_names: list,
358
- dimension_filters: list,
359
- secret_id: str,
360
- backoff_attempts: int,
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
- report_id=report["id"],
384
- file_id=run_report["id"],
385
- secret_id=secret_id,
386
- backoff_attempts=backoff_attempts,
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
- self._URL + "/bulk-list-line-item-assigned-targeting-options"
23
+ self._URL + "/bulk-list-line-item-assigned-targeting-options"
24
24
  )
25
25
  self._BULK_EDIT_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS = (
26
- self._URL + "/bulk-edit-line-item-assigned-targeting-options"
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
- self,
64
- advertiser_id,
65
- secret_id,
66
- line_item_ids,
67
- filter_string,
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._BULK_EDIT_LINE_ITEM_ASSIGNED_TARGETING_OPTIONS)
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
- self,
100
- advertiser_id,
101
- secret_id,
102
- line_item_ids,
103
- delete_requests=None,
104
- create_requests=None,
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
- self, advertiser_id, secret_id, channel_id, filter_string=None
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
- self,
217
- advertiser_id,
218
- secret_id,
219
- channel_id,
220
- deleted_sites=None,
221
- created_sites=None,
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
- self, secret_id: str, query_id: str, report_id: str, backoff_attempts: int = 10
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
- secret_id, query_id, report_id
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,