logos-sdk 0.0.25.dev9__tar.gz → 0.0.25.dev11__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.dev9 → logos_sdk-0.0.25.dev11}/PKG-INFO +10 -2
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/CampaignManager.py +105 -33
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/DV360.py +99 -24
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/Facebook.py +28 -1
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/GoogleAds.py +87 -18
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/MerchantCenter.py +42 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/MicrosoftAdvertising.py +26 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/Sklik.py +113 -36
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk.egg-info/PKG-INFO +10 -2
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/LICENSE +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/README.md +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/__init__.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/big_query/BigQuery.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/big_query/__init__.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/logging/LogosLogger.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/logging/__init__.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/Collabim.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/GoogleSheets.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/MarketMiner.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk/services/__init__.py +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk.egg-info/SOURCES.txt +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk.egg-info/dependency_links.txt +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk.egg-info/requires.txt +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/logos_sdk.egg-info/top_level.txt +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/setup.cfg +0 -0
- {logos_sdk-0.0.25.dev9 → logos_sdk-0.0.25.dev11}/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.dev11
|
|
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
|
|
|
@@ -30,17 +30,20 @@ class CampaignManagerService:
|
|
|
30
30
|
self._GET_FILE = self._URL + "/get-file"
|
|
31
31
|
self._GET_FILE_MEDIA_REQUEST = self._URL + "/get-file-media-request"
|
|
32
32
|
self._QUERY_DIMENSION_VALUES = self._URL + "/query-dimension-values"
|
|
33
|
+
self._GET_ACCESSIBLE_PARENT_ACCOUNTS = self._URL + "/get-parent-accounts"
|
|
34
|
+
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-advertisers"
|
|
35
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
33
36
|
|
|
34
37
|
def create_report(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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,
|
|
44
47
|
) -> Dict:
|
|
45
48
|
"""
|
|
46
49
|
Method for creating a report in API and returning a dict containing its ID for further use
|
|
@@ -179,7 +182,7 @@ class CampaignManagerService:
|
|
|
179
182
|
raise CampaignManagerServiceException(response.content)
|
|
180
183
|
|
|
181
184
|
def check_report_ready_with_exponential_backoff(
|
|
182
|
-
|
|
185
|
+
self, report_id: str, file_id: str, secret_id: str, backoff_attempts: int = 10
|
|
183
186
|
) -> bool:
|
|
184
187
|
"""
|
|
185
188
|
Implements exponential backoff for pooling the API for readiness of the report, suggested in
|
|
@@ -194,7 +197,7 @@ class CampaignManagerService:
|
|
|
194
197
|
if self.check_report_ready(report_id, file_id, secret_id):
|
|
195
198
|
return True
|
|
196
199
|
else:
|
|
197
|
-
time.sleep((2**attempt) + randint(1, 20))
|
|
200
|
+
time.sleep((2 ** attempt) + randint(1, 20))
|
|
198
201
|
|
|
199
202
|
return False
|
|
200
203
|
|
|
@@ -288,14 +291,14 @@ class CampaignManagerService:
|
|
|
288
291
|
raise CampaignManagerServiceException(response.content)
|
|
289
292
|
|
|
290
293
|
def query_dimension_values(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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,
|
|
299
302
|
) -> List[Dict]:
|
|
300
303
|
"""
|
|
301
304
|
Retrieves values of selected dimensions for data filtered according to rules set in list of filters
|
|
@@ -346,16 +349,16 @@ class CampaignManagerService:
|
|
|
346
349
|
yield service_response["data"]["items"]
|
|
347
350
|
|
|
348
351
|
def create_and_get_report_results(
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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,
|
|
359
362
|
) -> Dict:
|
|
360
363
|
"""
|
|
361
364
|
Method to create report, run it and fetch results in one go
|
|
@@ -378,10 +381,10 @@ class CampaignManagerService:
|
|
|
378
381
|
secret_id=secret_id,
|
|
379
382
|
)
|
|
380
383
|
if self.check_report_ready_with_exponential_backoff(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
384
|
+
report_id=report["id"],
|
|
385
|
+
file_id=run_report["id"],
|
|
386
|
+
secret_id=secret_id,
|
|
387
|
+
backoff_attempts=backoff_attempts,
|
|
385
388
|
):
|
|
386
389
|
return self.get_report_results(
|
|
387
390
|
report_id=report["id"],
|
|
@@ -397,3 +400,72 @@ class CampaignManagerService:
|
|
|
397
400
|
report_id=report["id"],
|
|
398
401
|
secret_id=secret_id,
|
|
399
402
|
)
|
|
403
|
+
|
|
404
|
+
def get_accessible_parent_accounts(self, secret_id):
|
|
405
|
+
"""
|
|
406
|
+
Returns accessible parent accounts
|
|
407
|
+
:param secret_id: The ID of the secret in secret manager
|
|
408
|
+
:return: list of parent accounts
|
|
409
|
+
"""
|
|
410
|
+
|
|
411
|
+
header = get_headers(self._GET_ACCESSIBLE_PARENT_ACCOUNTS)
|
|
412
|
+
body = {"secret_id": secret_id}
|
|
413
|
+
|
|
414
|
+
response = request(
|
|
415
|
+
"post", url=self._GET_ACCESSIBLE_PARENT_ACCOUNTS, json=body, headers=header
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
if response.status_code == HTTPStatus.OK:
|
|
419
|
+
service_response = response.json()
|
|
420
|
+
return service_response["data"]
|
|
421
|
+
else:
|
|
422
|
+
raise CampaignManagerServiceException(response.content)
|
|
423
|
+
|
|
424
|
+
def get_accessible_advertisers(self, secret_id):
|
|
425
|
+
"""
|
|
426
|
+
Returns accessible advertisers
|
|
427
|
+
:param secret_id: The ID of the secret in secret manager
|
|
428
|
+
:return: list of advertisers
|
|
429
|
+
"""
|
|
430
|
+
|
|
431
|
+
header = get_headers(self._GET_ACCESSIBLE_ADVERTISERS)
|
|
432
|
+
body = {
|
|
433
|
+
"secret_id": secret_id,
|
|
434
|
+
"filters": {
|
|
435
|
+
"hide_inactive": True,
|
|
436
|
+
},
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
response = request(
|
|
440
|
+
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
if response.status_code == HTTPStatus.OK:
|
|
444
|
+
service_response = response.json()
|
|
445
|
+
return service_response["data"]
|
|
446
|
+
else:
|
|
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 = 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)
|
|
@@ -20,10 +20,10 @@ class DV360Service:
|
|
|
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"
|
|
@@ -34,6 +34,9 @@ class DV360Service:
|
|
|
34
34
|
self._GET_QUERY_METADATA = self._URL + "/get-query-metadata"
|
|
35
35
|
self._RUN_QUERY = self._URL + "/run-query"
|
|
36
36
|
self._DELETE_QUERY = self._URL + "/delete-query"
|
|
37
|
+
self._GET_ACCESSIBLE_PARTNERS = self._URL + "/get-accessible-partners"
|
|
38
|
+
self._GET_ACCESSIBLE_ADVERTISERS = self._URL + "/get-accessible-advertisers"
|
|
39
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
37
40
|
|
|
38
41
|
def list_line_items(self, advertiser_id, secret_id, filter_string=None):
|
|
39
42
|
"""
|
|
@@ -58,11 +61,11 @@ class DV360Service:
|
|
|
58
61
|
raise DV360ServiceException(response.content)
|
|
59
62
|
|
|
60
63
|
def bulk_list_line_item_assigned_targeting_options(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
self,
|
|
65
|
+
advertiser_id,
|
|
66
|
+
secret_id,
|
|
67
|
+
line_item_ids,
|
|
68
|
+
filter_string,
|
|
66
69
|
):
|
|
67
70
|
"""
|
|
68
71
|
Bulk lists targeting options under multiple line items
|
|
@@ -94,12 +97,12 @@ class DV360Service:
|
|
|
94
97
|
raise DV360ServiceException(response.content)
|
|
95
98
|
|
|
96
99
|
def bulk_edit_line_item_assigned_targeting_options(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
self,
|
|
101
|
+
advertiser_id,
|
|
102
|
+
secret_id,
|
|
103
|
+
line_item_ids,
|
|
104
|
+
delete_requests=None,
|
|
105
|
+
create_requests=None,
|
|
103
106
|
):
|
|
104
107
|
"""
|
|
105
108
|
Bulk edits targeting options under multiple line items
|
|
@@ -180,7 +183,7 @@ class DV360Service:
|
|
|
180
183
|
raise DV360ServiceException(response.content)
|
|
181
184
|
|
|
182
185
|
def list_channel_sites(
|
|
183
|
-
|
|
186
|
+
self, advertiser_id, secret_id, channel_id, filter_string=None
|
|
184
187
|
):
|
|
185
188
|
"""
|
|
186
189
|
Lists channels for advertiser
|
|
@@ -211,12 +214,12 @@ class DV360Service:
|
|
|
211
214
|
raise DV360ServiceException(response.content)
|
|
212
215
|
|
|
213
216
|
def bulk_edit_channels_sites(
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
self,
|
|
218
|
+
advertiser_id,
|
|
219
|
+
secret_id,
|
|
220
|
+
channel_id,
|
|
221
|
+
deleted_sites=None,
|
|
222
|
+
created_sites=None,
|
|
220
223
|
):
|
|
221
224
|
"""
|
|
222
225
|
Bulk edits sites under a single channel
|
|
@@ -301,8 +304,80 @@ class DV360Service:
|
|
|
301
304
|
else:
|
|
302
305
|
raise DV360ServiceException(response.content)
|
|
303
306
|
|
|
307
|
+
def get_accessible_partners(self, secret_id):
|
|
308
|
+
"""
|
|
309
|
+
Returns accessible partners
|
|
310
|
+
:param secret_id: The ID of the secret in secret manager
|
|
311
|
+
:return: list of partners
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
header = get_headers(self._GET_ACCESSIBLE_PARTNERS)
|
|
315
|
+
body = {
|
|
316
|
+
"secret_id": secret_id,
|
|
317
|
+
"filters": {
|
|
318
|
+
"hide_inactive": True,
|
|
319
|
+
},
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
response = request(
|
|
323
|
+
"post", url=self._GET_ACCESSIBLE_PARTNERS, json=body, headers=header
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if response.status_code == HTTPStatus.OK:
|
|
327
|
+
service_response = response.json()
|
|
328
|
+
return service_response["data"]
|
|
329
|
+
else:
|
|
330
|
+
raise DV360ServiceException(response.content)
|
|
331
|
+
|
|
332
|
+
def get_accessible_advertisers(self, secret_id):
|
|
333
|
+
"""
|
|
334
|
+
Returns accessible advertisers
|
|
335
|
+
:param secret_id: The ID of the secret in secret manager
|
|
336
|
+
:return: list of advertisers
|
|
337
|
+
"""
|
|
338
|
+
|
|
339
|
+
header = get_headers(self._GET_ACCESSIBLE_ADVERTISERS)
|
|
340
|
+
body = {
|
|
341
|
+
"secret_id": secret_id,
|
|
342
|
+
"filters": {
|
|
343
|
+
"hide_inactive": True,
|
|
344
|
+
},
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
response = request(
|
|
348
|
+
"post", url=self._GET_ACCESSIBLE_ADVERTISERS, json=body, headers=header
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
if response.status_code == HTTPStatus.OK:
|
|
352
|
+
service_response = response.json()
|
|
353
|
+
return service_response["data"]
|
|
354
|
+
else:
|
|
355
|
+
raise DV360ServiceException(response.content)
|
|
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 = 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
|
+
|
|
304
379
|
def _check_report_ready_with_exponential_backoff(
|
|
305
|
-
|
|
380
|
+
self, secret_id: str, query_id: str, report_id: str, backoff_attempts: int = 10
|
|
306
381
|
) -> bool:
|
|
307
382
|
"""
|
|
308
383
|
Implements exponential backoff for pooling the API for readiness of the report, suggested in
|
|
@@ -318,7 +393,7 @@ class DV360Service:
|
|
|
318
393
|
elif state == "FAILED":
|
|
319
394
|
raise DV360ServiceException("Report failed to generate")
|
|
320
395
|
else:
|
|
321
|
-
sleep((2**attempt) + randint(1, 20))
|
|
396
|
+
sleep((2 ** attempt) + randint(1, 20))
|
|
322
397
|
|
|
323
398
|
return False
|
|
324
399
|
|
|
@@ -352,7 +427,7 @@ class DV360Service:
|
|
|
352
427
|
:return: The results
|
|
353
428
|
"""
|
|
354
429
|
if not self._check_report_ready_with_exponential_backoff(
|
|
355
|
-
|
|
430
|
+
secret_id, query_id, report_id
|
|
356
431
|
):
|
|
357
432
|
raise DV360ServiceException("Report did not generate in time")
|
|
358
433
|
|
|
@@ -393,5 +468,5 @@ class DV360Service:
|
|
|
393
468
|
|
|
394
469
|
if response.status_code == HTTPStatus.OK:
|
|
395
470
|
return True
|
|
396
|
-
|
|
471
|
+
|
|
397
472
|
return False
|
|
@@ -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
|
|
@@ -38,7 +39,12 @@ class FacebookService:
|
|
|
38
39
|
:return: all accessible accounts List(Dict)
|
|
39
40
|
"""
|
|
40
41
|
|
|
41
|
-
body = {
|
|
42
|
+
body = {
|
|
43
|
+
"secret_id": secret_id,
|
|
44
|
+
"filters": {
|
|
45
|
+
"hide_inactive": True,
|
|
46
|
+
}
|
|
47
|
+
}
|
|
42
48
|
|
|
43
49
|
header = get_headers(self._ACCESSIBLE_ACCOUNTS)
|
|
44
50
|
|
|
@@ -56,6 +62,27 @@ class FacebookService:
|
|
|
56
62
|
else:
|
|
57
63
|
raise FacebookServiceException(response.content)
|
|
58
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 = 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)
|
|
59
86
|
def get_insights(
|
|
60
87
|
self,
|
|
61
88
|
account_id: str,
|
|
@@ -21,6 +21,8 @@ class GoogleAdsService:
|
|
|
21
21
|
self._SEARCH = self._URL + "/search"
|
|
22
22
|
self._EXCLUDE_FOR_ACCOUNT = self._URL + "/exclude-for-account"
|
|
23
23
|
self._EXCLUDE_FOR_AD_GROUP = self._URL + "/exclude-for-ad-group"
|
|
24
|
+
self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/list-accessible-accounts"
|
|
25
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
24
26
|
|
|
25
27
|
@staticmethod
|
|
26
28
|
def fetch_with_retry_on_timeout(url, json, headers):
|
|
@@ -29,16 +31,18 @@ class GoogleAdsService:
|
|
|
29
31
|
return request("post", url, json=json, headers=headers, timeout=25)
|
|
30
32
|
except Timeout:
|
|
31
33
|
delay = 2 * (2 ** attempt) + random.randint(0, 9)
|
|
32
|
-
print(
|
|
34
|
+
print(
|
|
35
|
+
f"there was a timeout when contacting the service, going to sleep for {delay} seconds"
|
|
36
|
+
)
|
|
33
37
|
time.sleep(delay)
|
|
34
38
|
|
|
35
39
|
raise Exception("The service is not able to reply within 30 seconds.")
|
|
36
40
|
|
|
37
41
|
def search_stream(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
self,
|
|
43
|
+
query: str,
|
|
44
|
+
queried_account_id: str,
|
|
45
|
+
secret_id: str,
|
|
42
46
|
) -> List[Union[List, Dict]]:
|
|
43
47
|
"""
|
|
44
48
|
:param query Sql query for google ads. Best way to build it is https://developers.google.com/google-ads/api/fields/v14/accessible_bidding_strategy_query_builder
|
|
@@ -53,7 +57,9 @@ class GoogleAdsService:
|
|
|
53
57
|
}
|
|
54
58
|
|
|
55
59
|
header = get_headers(self._SEARCH_STREAM)
|
|
56
|
-
response = self.fetch_with_retry_on_timeout(
|
|
60
|
+
response = self.fetch_with_retry_on_timeout(
|
|
61
|
+
url=self._SEARCH_STREAM, json=body, headers=header
|
|
62
|
+
)
|
|
57
63
|
|
|
58
64
|
if response.status_code == HTTPStatus.OK:
|
|
59
65
|
service_response = response.json()
|
|
@@ -62,11 +68,11 @@ class GoogleAdsService:
|
|
|
62
68
|
raise GoogleAdsServiceException(response.content)
|
|
63
69
|
|
|
64
70
|
def search(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
self,
|
|
72
|
+
query: str,
|
|
73
|
+
queried_account_id: str,
|
|
74
|
+
secret_id: str,
|
|
75
|
+
page_size: int,
|
|
70
76
|
) -> List[Dict]:
|
|
71
77
|
"""
|
|
72
78
|
:param query Sql query for google ads. Best way to build it is https://developers.google.com/google-ads/api/fields/v14/accessible_bidding_strategy_query_builder
|
|
@@ -84,7 +90,9 @@ class GoogleAdsService:
|
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
header = get_headers(self._SEARCH)
|
|
87
|
-
response = self.fetch_with_retry_on_timeout(
|
|
93
|
+
response = self.fetch_with_retry_on_timeout(
|
|
94
|
+
url=self._SEARCH, json=body, headers=header
|
|
95
|
+
)
|
|
88
96
|
|
|
89
97
|
if response.status_code != HTTPStatus.OK:
|
|
90
98
|
raise GoogleAdsServiceException(response.content)
|
|
@@ -104,7 +112,7 @@ class GoogleAdsService:
|
|
|
104
112
|
yield service_response["data"]["results"]
|
|
105
113
|
|
|
106
114
|
def exclude_criterion_for_account(
|
|
107
|
-
|
|
115
|
+
self, client_id: str, exclusion_raw: List[str], mode: str, secret_id: str = None
|
|
108
116
|
) -> None:
|
|
109
117
|
"""
|
|
110
118
|
Excludes list of unwanted urls/YouTube channels for account with client_id
|
|
@@ -133,11 +141,11 @@ class GoogleAdsService:
|
|
|
133
141
|
raise GoogleAdsServiceException(response.content)
|
|
134
142
|
|
|
135
143
|
def exclude_criterion_for_ad_group(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
144
|
+
self,
|
|
145
|
+
client_id: str,
|
|
146
|
+
ad_group_id: str,
|
|
147
|
+
exclusion_raw: List[str],
|
|
148
|
+
secret_id: str = None,
|
|
141
149
|
) -> None:
|
|
142
150
|
"""
|
|
143
151
|
Excludes list of unwanted urls/YouTube channels for given ad_group with client_id
|
|
@@ -163,3 +171,64 @@ class GoogleAdsService:
|
|
|
163
171
|
return
|
|
164
172
|
else:
|
|
165
173
|
raise GoogleAdsServiceException(response.content)
|
|
174
|
+
|
|
175
|
+
def get_accessible_accounts(
|
|
176
|
+
self,
|
|
177
|
+
secret_id: str,
|
|
178
|
+
page_size: int = 1000,
|
|
179
|
+
) -> List[Dict]:
|
|
180
|
+
body = {
|
|
181
|
+
"secret_id": secret_id,
|
|
182
|
+
"filters": {
|
|
183
|
+
"hide_inactive": True,
|
|
184
|
+
},
|
|
185
|
+
"page_token": None,
|
|
186
|
+
"page_size": page_size,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
header = get_headers(self._GET_ACCESSIBLE_ACCOUNTS)
|
|
190
|
+
response = self.fetch_with_retry_on_timeout(
|
|
191
|
+
url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
if response.status_code != HTTPStatus.OK:
|
|
195
|
+
raise GoogleAdsServiceException(response.content)
|
|
196
|
+
|
|
197
|
+
service_response = response.json()
|
|
198
|
+
all_results = service_response["data"]["results"]
|
|
199
|
+
|
|
200
|
+
while service_response["data"]["next_page_token"]:
|
|
201
|
+
body["page_token"] = service_response["data"]["next_page_token"]
|
|
202
|
+
response = request(
|
|
203
|
+
"post", url=self._GET_ACCESSIBLE_ACCOUNTS, json=body, headers=header
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
if response.status_code != HTTPStatus.OK:
|
|
207
|
+
raise GoogleAdsServiceException(response.content)
|
|
208
|
+
|
|
209
|
+
service_response = response.json()
|
|
210
|
+
all_results.extend(service_response["data"]["results"])
|
|
211
|
+
|
|
212
|
+
return all_results
|
|
213
|
+
|
|
214
|
+
def get_account_accessibility(self, secret_id: str, account_id: str) -> bool:
|
|
215
|
+
"""
|
|
216
|
+
Gets account accessibility
|
|
217
|
+
:param secret_id: The ID of the secret in secret manager
|
|
218
|
+
:param account_id: The ID of the account.
|
|
219
|
+
:return: True if account has access
|
|
220
|
+
"""
|
|
221
|
+
body = {
|
|
222
|
+
"secret_id": secret_id,
|
|
223
|
+
"account_id": account_id
|
|
224
|
+
}
|
|
225
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
226
|
+
response = request(
|
|
227
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
if response.status_code == HTTPStatus.OK:
|
|
231
|
+
service_response = response.json()
|
|
232
|
+
return service_response["data"]
|
|
233
|
+
else:
|
|
234
|
+
raise GoogleAdsServiceException(response.content)
|
|
@@ -14,6 +14,8 @@ class MerchantCenterService:
|
|
|
14
14
|
load_dotenv()
|
|
15
15
|
self._URL = url or os.environ.get("MERCHANT_CENTER_SERVICE_PATH")
|
|
16
16
|
self._LIST_ACCOUNTS = self._URL + "/account-service/accounts"
|
|
17
|
+
self._LIST_ACCESSIBLE_ACCOUNTS = self._URL + "/account-service/list-accessible-accounts"
|
|
18
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/account-service/get-account-accessibility"
|
|
17
19
|
self._LIST_ACCOUNT_STATUSES = self._URL + "/account-service/account-statuses"
|
|
18
20
|
self._LIST_PRODUCTS = self._URL + "/product-service/products"
|
|
19
21
|
self._LIST_PRODUCT_STATUSES = self._URL + "/product-service/product-statuses"
|
|
@@ -36,6 +38,22 @@ class MerchantCenterService:
|
|
|
36
38
|
else:
|
|
37
39
|
raise MerchantServiceException(response.content)
|
|
38
40
|
|
|
41
|
+
def list_accessible_accounts(self, secret_id: str):
|
|
42
|
+
"""
|
|
43
|
+
Lists accessible accounts in Merchant Center
|
|
44
|
+
:param secret_id: The ID of the secret in secret manager
|
|
45
|
+
:return: List[Dict]
|
|
46
|
+
"""
|
|
47
|
+
body = {"secret_id": secret_id}
|
|
48
|
+
header = get_headers(self._LIST_ACCESSIBLE_ACCOUNTS)
|
|
49
|
+
response = request("post", url=self._LIST_ACCESSIBLE_ACCOUNTS, json=body, headers=header)
|
|
50
|
+
|
|
51
|
+
if response.status_code == HTTPStatus.OK:
|
|
52
|
+
service_response = response.json()
|
|
53
|
+
return service_response["data"]
|
|
54
|
+
else:
|
|
55
|
+
raise MerchantServiceException(response.content)
|
|
56
|
+
|
|
39
57
|
def list_account_statuses(
|
|
40
58
|
self, merchant_account_id: str, account_id: str, secret_id: str
|
|
41
59
|
):
|
|
@@ -182,3 +200,27 @@ class MerchantCenterService:
|
|
|
182
200
|
|
|
183
201
|
service_response = response.json()
|
|
184
202
|
yield service_response["data"]["results"]
|
|
203
|
+
|
|
204
|
+
def get_account_accessibility(self, secret_id: str,merchant_account_id:str, account_id: str) -> bool:
|
|
205
|
+
"""
|
|
206
|
+
Gets account accessibility
|
|
207
|
+
:param secret_id: The ID of the secret in secret manager
|
|
208
|
+
:param merchant_account_id: The ID of the managing account. This must be a multi-client account.
|
|
209
|
+
:param account_id: The ID of the account.
|
|
210
|
+
:return: True if account has access
|
|
211
|
+
"""
|
|
212
|
+
body = {
|
|
213
|
+
"secret_id": secret_id,
|
|
214
|
+
"merchant_account_id": merchant_account_id,
|
|
215
|
+
"account_id": account_id
|
|
216
|
+
}
|
|
217
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
218
|
+
response = request(
|
|
219
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
if response.status_code == HTTPStatus.OK:
|
|
223
|
+
service_response = response.json()
|
|
224
|
+
return service_response["data"]
|
|
225
|
+
else:
|
|
226
|
+
raise MerchantServiceException(response.content)
|
|
@@ -23,6 +23,7 @@ class MicrosoftAdvertising:
|
|
|
23
23
|
load_dotenv()
|
|
24
24
|
self._URL = url or os.environ.get("MICROSOFT_ADVERTISING_PATH")
|
|
25
25
|
self._GET_ACCESSIBLE_ACCOUNTS = self._URL + "/accessible-accounts"
|
|
26
|
+
self._GET_ACCOUNT_ACCESSIBILITY = self._URL + "/get-account-accessibility"
|
|
26
27
|
self._GET_DESTINATION_URL_REPORT = self._URL + "/destination-url-report"
|
|
27
28
|
self._GET_CAMPAIGN_PERFORMANCE_REPORT = self._URL + "/campaign-performance-report"
|
|
28
29
|
self._GET_GEOGRAPHIC_PERFORMANCE_REPORT = self._URL + "/geographic-performance-report"
|
|
@@ -212,6 +213,9 @@ class MicrosoftAdvertising:
|
|
|
212
213
|
"""
|
|
213
214
|
body = {
|
|
214
215
|
"secret_id": secret_id,
|
|
216
|
+
"filters": {
|
|
217
|
+
"hide_inactive": True
|
|
218
|
+
}
|
|
215
219
|
}
|
|
216
220
|
header = get_headers(self._GET_ACCESSIBLE_ACCOUNTS)
|
|
217
221
|
response = request(
|
|
@@ -223,3 +227,25 @@ class MicrosoftAdvertising:
|
|
|
223
227
|
return service_response["data"]
|
|
224
228
|
else:
|
|
225
229
|
raise MicrosoftAdvertisingException(response.content)
|
|
230
|
+
|
|
231
|
+
def get_account_accessibility(self, secret_id: str, account_id: str) -> bool:
|
|
232
|
+
"""
|
|
233
|
+
Gets account accessibility
|
|
234
|
+
:param secret_id: The ID of the secret in secret manager
|
|
235
|
+
:param account_id: The ID of the account in Microsoft Advertising
|
|
236
|
+
:return: True if account has access
|
|
237
|
+
"""
|
|
238
|
+
body = {
|
|
239
|
+
"secret_id": secret_id,
|
|
240
|
+
"account_id": account_id
|
|
241
|
+
}
|
|
242
|
+
header = get_headers(self._GET_ACCOUNT_ACCESSIBILITY)
|
|
243
|
+
response = request(
|
|
244
|
+
"post", url=self._GET_ACCOUNT_ACCESSIBILITY, json=body, headers=header
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if response.status_code == HTTPStatus.OK:
|
|
248
|
+
service_response = response.json()
|
|
249
|
+
return service_response["data"]
|
|
250
|
+
else:
|
|
251
|
+
raise MicrosoftAdvertisingException(response.content)
|
|
@@ -46,13 +46,15 @@ def get_session_if_malformed(wrapped_function):
|
|
|
46
46
|
|
|
47
47
|
return inner
|
|
48
48
|
|
|
49
|
+
|
|
49
50
|
def get_report_results_if_expired(wrapped_function):
|
|
50
51
|
@wraps(wrapped_function)
|
|
51
52
|
def inner(*args, **kwargs):
|
|
52
53
|
try:
|
|
53
54
|
return wrapped_function(*args, **kwargs)
|
|
54
55
|
except SklikServiceException as err:
|
|
55
|
-
if json.loads(err.args[0].decode("utf8")).get(
|
|
56
|
+
if json.loads(err.args[0].decode("utf8")).get(
|
|
57
|
+
"detail") == "Requested report has expired. Please create a new report by calling createReport endpoint.":
|
|
56
58
|
print("Report expired, creating new report")
|
|
57
59
|
args[0].get_session(args[0]._secret_id, args[0]._account_email)
|
|
58
60
|
return wrapped_function(*args, **kwargs)
|
|
@@ -74,6 +76,7 @@ class SklikService:
|
|
|
74
76
|
self._LOGOUT = self._URL + "/logout"
|
|
75
77
|
self._API_LIMITS = self._URL + "/api-limits"
|
|
76
78
|
self._GET_SESSION = self._URL + "/get-session"
|
|
79
|
+
self._GET_SESSION_WITHOUT_ACCOUNT = self._URL + "/get-session-without-account"
|
|
77
80
|
self.session = None # dict {"sklik_session": "", "user_id": ""}
|
|
78
81
|
self._secret_id = None
|
|
79
82
|
self._account_email = None
|
|
@@ -99,6 +102,25 @@ class SklikService:
|
|
|
99
102
|
else:
|
|
100
103
|
raise SklikServiceException(response.content)
|
|
101
104
|
|
|
105
|
+
def get_session_without_account(self, secret_id: str) -> None:
|
|
106
|
+
"""
|
|
107
|
+
Manages initial login of the sessions, returns dict with sklik session string without user id int
|
|
108
|
+
:param secret_id: The ID of the secret in secret manager
|
|
109
|
+
:return: Dict
|
|
110
|
+
"""
|
|
111
|
+
body = {"secret_id": secret_id}
|
|
112
|
+
response = execute_request("post", url=self._GET_SESSION_WITHOUT_ACCOUNT, json=body)
|
|
113
|
+
|
|
114
|
+
if response.status_code == HTTPStatus.OK:
|
|
115
|
+
service_response = response.json()
|
|
116
|
+
self.session = {
|
|
117
|
+
"sklik_session": service_response["data"]["sklik_session"],
|
|
118
|
+
"user_id": None,
|
|
119
|
+
}
|
|
120
|
+
self._secret_id = secret_id
|
|
121
|
+
else:
|
|
122
|
+
raise SklikServiceException(response.content)
|
|
123
|
+
|
|
102
124
|
def logout_of_current_session(self) -> None:
|
|
103
125
|
"""
|
|
104
126
|
Attempts to log out of the current session, nothing happens if not ok
|
|
@@ -113,15 +135,15 @@ class SklikService:
|
|
|
113
135
|
@get_session_if_malformed
|
|
114
136
|
@get_report_results_if_expired
|
|
115
137
|
def get_report_results(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
138
|
+
self,
|
|
139
|
+
secret_id: str,
|
|
140
|
+
account_email: str,
|
|
141
|
+
report_type: str,
|
|
142
|
+
date_from: str,
|
|
143
|
+
date_to: str,
|
|
144
|
+
columns: List[str],
|
|
145
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
146
|
+
read_params: Dict[str, Union[int, str]] = None,
|
|
125
147
|
) -> List[Dict]:
|
|
126
148
|
"""
|
|
127
149
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -166,14 +188,14 @@ class SklikService:
|
|
|
166
188
|
|
|
167
189
|
@get_session_if_malformed
|
|
168
190
|
def get_streamed_report_results(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
191
|
+
self,
|
|
192
|
+
secret_id: str,
|
|
193
|
+
account_email: str,
|
|
194
|
+
report_type: str,
|
|
195
|
+
date_from: str,
|
|
196
|
+
date_to: str,
|
|
197
|
+
columns: List[str],
|
|
198
|
+
create_params: Dict[str, Union[int, str]] = None,
|
|
177
199
|
) -> List[Dict]:
|
|
178
200
|
"""
|
|
179
201
|
It creates sklik report with /create-report call in sklik service and reads sklik report with pagination
|
|
@@ -213,13 +235,13 @@ class SklikService:
|
|
|
213
235
|
raise SklikServiceException(response.content)
|
|
214
236
|
|
|
215
237
|
def _create_report(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
238
|
+
self,
|
|
239
|
+
secret_id: str,
|
|
240
|
+
account_email: str,
|
|
241
|
+
report_type: str,
|
|
242
|
+
date_from: str,
|
|
243
|
+
date_to: str,
|
|
244
|
+
params: Dict[str, Union[int, str]] = None,
|
|
223
245
|
) -> Dict:
|
|
224
246
|
"""
|
|
225
247
|
Function creates sklik report with /create-report call
|
|
@@ -256,7 +278,7 @@ class SklikService:
|
|
|
256
278
|
|
|
257
279
|
@get_session_if_malformed
|
|
258
280
|
def call_api(
|
|
259
|
-
|
|
281
|
+
self, secret_id: str, account_email: str, method: str, params: List[Dict]
|
|
260
282
|
) -> Union[List, Dict]:
|
|
261
283
|
"""
|
|
262
284
|
Function to call SklikService get-api route
|
|
@@ -310,12 +332,52 @@ class SklikService:
|
|
|
310
332
|
return service_response["data"]
|
|
311
333
|
else:
|
|
312
334
|
raise SklikServiceException(response.content)
|
|
335
|
+
|
|
336
|
+
@get_session_if_malformed
|
|
337
|
+
def get_accessible_accounts(
|
|
338
|
+
self,
|
|
339
|
+
secret_id: str,
|
|
340
|
+
) -> Union[List, Dict]:
|
|
341
|
+
"""
|
|
342
|
+
Function to retrieve accessible accounts
|
|
343
|
+
:param secret_id: The ID of the secret in secret manager
|
|
344
|
+
:return:
|
|
345
|
+
"""
|
|
346
|
+
|
|
347
|
+
if self.session is None:
|
|
348
|
+
self.get_session_without_account(secret_id)
|
|
349
|
+
|
|
350
|
+
response = execute_request(
|
|
351
|
+
"post",
|
|
352
|
+
url=self._GET_CLIENT,
|
|
353
|
+
json={
|
|
354
|
+
"sklik_session": self.session["sklik_session"],
|
|
355
|
+
"filters": {
|
|
356
|
+
"hide_inactive": True
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if response.status_code == HTTPStatus.OK:
|
|
362
|
+
accounts = []
|
|
363
|
+
service_response = response.json()
|
|
364
|
+
data = service_response["data"]
|
|
365
|
+
for account in data["foreignAccounts"]:
|
|
366
|
+
accounts.append({
|
|
367
|
+
"id": account['userId'],
|
|
368
|
+
"name": account['username'],
|
|
369
|
+
"active": account["relationStatus"] == "live"
|
|
370
|
+
})
|
|
371
|
+
return accounts
|
|
372
|
+
else:
|
|
373
|
+
raise SklikServiceException(response.content)
|
|
374
|
+
|
|
313
375
|
@get_session_if_malformed
|
|
314
376
|
def check_data_ready(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
377
|
+
self,
|
|
378
|
+
secret_id: str,
|
|
379
|
+
account_email: str,
|
|
380
|
+
date: str = None,
|
|
319
381
|
) -> int:
|
|
320
382
|
"""
|
|
321
383
|
Checks if data on server are ready
|
|
@@ -329,8 +391,8 @@ class SklikService:
|
|
|
329
391
|
self.get_session(secret_id, account_email)
|
|
330
392
|
|
|
331
393
|
date = date or (
|
|
332
|
-
|
|
333
|
-
|
|
394
|
+
datetime.now(timezone("UTC")).astimezone(timezone("Europe/Prague"))
|
|
395
|
+
- timedelta(days=1)
|
|
334
396
|
).strftime("%Y-%m-%d")
|
|
335
397
|
|
|
336
398
|
body = {"date": date}
|
|
@@ -351,9 +413,9 @@ class SklikService:
|
|
|
351
413
|
|
|
352
414
|
@get_session_if_malformed
|
|
353
415
|
def fetch_api_limits(
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
416
|
+
self,
|
|
417
|
+
secret_id: str,
|
|
418
|
+
account_email: str,
|
|
357
419
|
) -> Union[List, Dict]:
|
|
358
420
|
"""
|
|
359
421
|
Function to call SklikService api/limits route
|
|
@@ -371,4 +433,19 @@ class SklikService:
|
|
|
371
433
|
service_response = response.json()
|
|
372
434
|
return service_response["data"]
|
|
373
435
|
else:
|
|
374
|
-
raise SklikServiceException(response.content)
|
|
436
|
+
raise SklikServiceException(response.content)
|
|
437
|
+
|
|
438
|
+
def get_account_accessibility(self, secret_id: str, account_email: str) -> bool:
|
|
439
|
+
"""
|
|
440
|
+
Gets account accessibility
|
|
441
|
+
:param secret_id: The ID of the secret in secret manager
|
|
442
|
+
:param account_email: Account email to refers to Sklik accountId
|
|
443
|
+
:return: True if account has access
|
|
444
|
+
"""
|
|
445
|
+
body = {"account_email": account_email, "secret_id": secret_id}
|
|
446
|
+
response = execute_request("post", url=self._GET_SESSION, json=body)
|
|
447
|
+
if response.status_code == 200:
|
|
448
|
+
return True
|
|
449
|
+
if response.status_code == 401 or response.status_code == 403 or response.status_code == 404:
|
|
450
|
+
return False
|
|
451
|
+
raise SklikServiceException(response.content)
|
|
@@ -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.dev11
|
|
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
|
|
|
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
|
|
File without changes
|