dtlpy 1.115.44__py3-none-any.whl → 1.116.6__py3-none-any.whl
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.
- dtlpy/__init__.py +491 -491
- dtlpy/__version__.py +1 -1
- dtlpy/assets/__init__.py +26 -26
- dtlpy/assets/code_server/config.yaml +2 -2
- dtlpy/assets/code_server/installation.sh +24 -24
- dtlpy/assets/code_server/launch.json +13 -13
- dtlpy/assets/code_server/settings.json +2 -2
- dtlpy/assets/main.py +53 -53
- dtlpy/assets/main_partial.py +18 -18
- dtlpy/assets/mock.json +11 -11
- dtlpy/assets/model_adapter.py +83 -83
- dtlpy/assets/package.json +61 -61
- dtlpy/assets/package_catalog.json +29 -29
- dtlpy/assets/package_gitignore +307 -307
- dtlpy/assets/service_runners/__init__.py +33 -33
- dtlpy/assets/service_runners/converter.py +96 -96
- dtlpy/assets/service_runners/multi_method.py +49 -49
- dtlpy/assets/service_runners/multi_method_annotation.py +54 -54
- dtlpy/assets/service_runners/multi_method_dataset.py +55 -55
- dtlpy/assets/service_runners/multi_method_item.py +52 -52
- dtlpy/assets/service_runners/multi_method_json.py +52 -52
- dtlpy/assets/service_runners/single_method.py +37 -37
- dtlpy/assets/service_runners/single_method_annotation.py +43 -43
- dtlpy/assets/service_runners/single_method_dataset.py +43 -43
- dtlpy/assets/service_runners/single_method_item.py +41 -41
- dtlpy/assets/service_runners/single_method_json.py +42 -42
- dtlpy/assets/service_runners/single_method_multi_input.py +45 -45
- dtlpy/assets/voc_annotation_template.xml +23 -23
- dtlpy/caches/base_cache.py +32 -32
- dtlpy/caches/cache.py +473 -473
- dtlpy/caches/dl_cache.py +201 -201
- dtlpy/caches/filesystem_cache.py +89 -89
- dtlpy/caches/redis_cache.py +84 -84
- dtlpy/dlp/__init__.py +20 -20
- dtlpy/dlp/cli_utilities.py +367 -367
- dtlpy/dlp/command_executor.py +764 -764
- dtlpy/dlp/dlp +1 -1
- dtlpy/dlp/dlp.bat +1 -1
- dtlpy/dlp/dlp.py +128 -128
- dtlpy/dlp/parser.py +651 -651
- dtlpy/entities/__init__.py +83 -83
- dtlpy/entities/analytic.py +347 -347
- dtlpy/entities/annotation.py +1879 -1879
- dtlpy/entities/annotation_collection.py +699 -699
- dtlpy/entities/annotation_definitions/__init__.py +20 -20
- dtlpy/entities/annotation_definitions/base_annotation_definition.py +100 -100
- dtlpy/entities/annotation_definitions/box.py +195 -195
- dtlpy/entities/annotation_definitions/classification.py +67 -67
- dtlpy/entities/annotation_definitions/comparison.py +72 -72
- dtlpy/entities/annotation_definitions/cube.py +204 -204
- dtlpy/entities/annotation_definitions/cube_3d.py +149 -149
- dtlpy/entities/annotation_definitions/description.py +32 -32
- dtlpy/entities/annotation_definitions/ellipse.py +124 -124
- dtlpy/entities/annotation_definitions/free_text.py +62 -62
- dtlpy/entities/annotation_definitions/gis.py +69 -69
- dtlpy/entities/annotation_definitions/note.py +139 -139
- dtlpy/entities/annotation_definitions/point.py +117 -117
- dtlpy/entities/annotation_definitions/polygon.py +182 -182
- dtlpy/entities/annotation_definitions/polyline.py +111 -111
- dtlpy/entities/annotation_definitions/pose.py +92 -92
- dtlpy/entities/annotation_definitions/ref_image.py +86 -86
- dtlpy/entities/annotation_definitions/segmentation.py +240 -240
- dtlpy/entities/annotation_definitions/subtitle.py +34 -34
- dtlpy/entities/annotation_definitions/text.py +85 -85
- dtlpy/entities/annotation_definitions/undefined_annotation.py +74 -74
- dtlpy/entities/app.py +220 -220
- dtlpy/entities/app_module.py +107 -107
- dtlpy/entities/artifact.py +174 -174
- dtlpy/entities/assignment.py +399 -399
- dtlpy/entities/base_entity.py +214 -214
- dtlpy/entities/bot.py +113 -113
- dtlpy/entities/codebase.py +292 -292
- dtlpy/entities/collection.py +38 -38
- dtlpy/entities/command.py +169 -169
- dtlpy/entities/compute.py +449 -449
- dtlpy/entities/dataset.py +1299 -1299
- dtlpy/entities/directory_tree.py +44 -44
- dtlpy/entities/dpk.py +470 -470
- dtlpy/entities/driver.py +235 -235
- dtlpy/entities/execution.py +397 -397
- dtlpy/entities/feature.py +124 -124
- dtlpy/entities/feature_set.py +145 -145
- dtlpy/entities/filters.py +798 -798
- dtlpy/entities/gis_item.py +107 -107
- dtlpy/entities/integration.py +184 -184
- dtlpy/entities/item.py +959 -959
- dtlpy/entities/label.py +123 -123
- dtlpy/entities/links.py +85 -85
- dtlpy/entities/message.py +175 -175
- dtlpy/entities/model.py +684 -684
- dtlpy/entities/node.py +1005 -1005
- dtlpy/entities/ontology.py +810 -803
- dtlpy/entities/organization.py +287 -287
- dtlpy/entities/package.py +657 -657
- dtlpy/entities/package_defaults.py +5 -5
- dtlpy/entities/package_function.py +185 -185
- dtlpy/entities/package_module.py +113 -113
- dtlpy/entities/package_slot.py +118 -118
- dtlpy/entities/paged_entities.py +299 -299
- dtlpy/entities/pipeline.py +624 -624
- dtlpy/entities/pipeline_execution.py +279 -279
- dtlpy/entities/project.py +394 -394
- dtlpy/entities/prompt_item.py +505 -505
- dtlpy/entities/recipe.py +301 -301
- dtlpy/entities/reflect_dict.py +102 -102
- dtlpy/entities/resource_execution.py +138 -138
- dtlpy/entities/service.py +963 -963
- dtlpy/entities/service_driver.py +117 -117
- dtlpy/entities/setting.py +294 -294
- dtlpy/entities/task.py +495 -495
- dtlpy/entities/time_series.py +143 -143
- dtlpy/entities/trigger.py +426 -426
- dtlpy/entities/user.py +118 -118
- dtlpy/entities/webhook.py +124 -124
- dtlpy/examples/__init__.py +19 -19
- dtlpy/examples/add_labels.py +135 -135
- dtlpy/examples/add_metadata_to_item.py +21 -21
- dtlpy/examples/annotate_items_using_model.py +65 -65
- dtlpy/examples/annotate_video_using_model_and_tracker.py +75 -75
- dtlpy/examples/annotations_convert_to_voc.py +9 -9
- dtlpy/examples/annotations_convert_to_yolo.py +9 -9
- dtlpy/examples/convert_annotation_types.py +51 -51
- dtlpy/examples/converter.py +143 -143
- dtlpy/examples/copy_annotations.py +22 -22
- dtlpy/examples/copy_folder.py +31 -31
- dtlpy/examples/create_annotations.py +51 -51
- dtlpy/examples/create_video_annotations.py +83 -83
- dtlpy/examples/delete_annotations.py +26 -26
- dtlpy/examples/filters.py +113 -113
- dtlpy/examples/move_item.py +23 -23
- dtlpy/examples/play_video_annotation.py +13 -13
- dtlpy/examples/show_item_and_mask.py +53 -53
- dtlpy/examples/triggers.py +49 -49
- dtlpy/examples/upload_batch_of_items.py +20 -20
- dtlpy/examples/upload_items_and_custom_format_annotations.py +55 -55
- dtlpy/examples/upload_items_with_modalities.py +43 -43
- dtlpy/examples/upload_segmentation_annotations_from_mask_image.py +44 -44
- dtlpy/examples/upload_yolo_format_annotations.py +70 -70
- dtlpy/exceptions.py +125 -125
- dtlpy/miscellaneous/__init__.py +20 -20
- dtlpy/miscellaneous/dict_differ.py +95 -95
- dtlpy/miscellaneous/git_utils.py +217 -217
- dtlpy/miscellaneous/json_utils.py +14 -14
- dtlpy/miscellaneous/list_print.py +105 -105
- dtlpy/miscellaneous/zipping.py +130 -130
- dtlpy/ml/__init__.py +20 -20
- dtlpy/ml/base_feature_extractor_adapter.py +27 -27
- dtlpy/ml/base_model_adapter.py +1257 -1230
- dtlpy/ml/metrics.py +461 -461
- dtlpy/ml/predictions_utils.py +274 -274
- dtlpy/ml/summary_writer.py +57 -57
- dtlpy/ml/train_utils.py +60 -60
- dtlpy/new_instance.py +252 -252
- dtlpy/repositories/__init__.py +56 -56
- dtlpy/repositories/analytics.py +85 -85
- dtlpy/repositories/annotations.py +916 -916
- dtlpy/repositories/apps.py +383 -383
- dtlpy/repositories/artifacts.py +452 -452
- dtlpy/repositories/assignments.py +599 -599
- dtlpy/repositories/bots.py +213 -213
- dtlpy/repositories/codebases.py +559 -559
- dtlpy/repositories/collections.py +332 -332
- dtlpy/repositories/commands.py +152 -152
- dtlpy/repositories/compositions.py +61 -61
- dtlpy/repositories/computes.py +439 -439
- dtlpy/repositories/datasets.py +1504 -1504
- dtlpy/repositories/downloader.py +976 -923
- dtlpy/repositories/dpks.py +433 -433
- dtlpy/repositories/drivers.py +482 -482
- dtlpy/repositories/executions.py +815 -815
- dtlpy/repositories/feature_sets.py +226 -226
- dtlpy/repositories/features.py +255 -255
- dtlpy/repositories/integrations.py +484 -484
- dtlpy/repositories/items.py +912 -912
- dtlpy/repositories/messages.py +94 -94
- dtlpy/repositories/models.py +1000 -1000
- dtlpy/repositories/nodes.py +80 -80
- dtlpy/repositories/ontologies.py +511 -511
- dtlpy/repositories/organizations.py +525 -525
- dtlpy/repositories/packages.py +1941 -1941
- dtlpy/repositories/pipeline_executions.py +451 -451
- dtlpy/repositories/pipelines.py +640 -640
- dtlpy/repositories/projects.py +539 -539
- dtlpy/repositories/recipes.py +419 -399
- dtlpy/repositories/resource_executions.py +137 -137
- dtlpy/repositories/schema.py +120 -120
- dtlpy/repositories/service_drivers.py +213 -213
- dtlpy/repositories/services.py +1704 -1704
- dtlpy/repositories/settings.py +339 -339
- dtlpy/repositories/tasks.py +1477 -1477
- dtlpy/repositories/times_series.py +278 -278
- dtlpy/repositories/triggers.py +536 -536
- dtlpy/repositories/upload_element.py +257 -257
- dtlpy/repositories/uploader.py +661 -661
- dtlpy/repositories/webhooks.py +249 -249
- dtlpy/services/__init__.py +22 -22
- dtlpy/services/aihttp_retry.py +131 -131
- dtlpy/services/api_client.py +1785 -1785
- dtlpy/services/api_reference.py +40 -40
- dtlpy/services/async_utils.py +133 -133
- dtlpy/services/calls_counter.py +44 -44
- dtlpy/services/check_sdk.py +68 -68
- dtlpy/services/cookie.py +115 -115
- dtlpy/services/create_logger.py +156 -156
- dtlpy/services/events.py +84 -84
- dtlpy/services/logins.py +235 -235
- dtlpy/services/reporter.py +256 -256
- dtlpy/services/service_defaults.py +91 -91
- dtlpy/utilities/__init__.py +20 -20
- dtlpy/utilities/annotations/__init__.py +16 -16
- dtlpy/utilities/annotations/annotation_converters.py +269 -269
- dtlpy/utilities/base_package_runner.py +285 -264
- dtlpy/utilities/converter.py +1650 -1650
- dtlpy/utilities/dataset_generators/__init__.py +1 -1
- dtlpy/utilities/dataset_generators/dataset_generator.py +670 -670
- dtlpy/utilities/dataset_generators/dataset_generator_tensorflow.py +23 -23
- dtlpy/utilities/dataset_generators/dataset_generator_torch.py +21 -21
- dtlpy/utilities/local_development/__init__.py +1 -1
- dtlpy/utilities/local_development/local_session.py +179 -179
- dtlpy/utilities/reports/__init__.py +2 -2
- dtlpy/utilities/reports/figures.py +343 -343
- dtlpy/utilities/reports/report.py +71 -71
- dtlpy/utilities/videos/__init__.py +17 -17
- dtlpy/utilities/videos/video_player.py +598 -598
- dtlpy/utilities/videos/videos.py +470 -470
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp +1 -1
- dtlpy-1.116.6.data/scripts/dlp.bat +2 -0
- {dtlpy-1.115.44.data → dtlpy-1.116.6.data}/scripts/dlp.py +128 -128
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/METADATA +186 -186
- dtlpy-1.116.6.dist-info/RECORD +239 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/WHEEL +1 -1
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/licenses/LICENSE +200 -200
- tests/features/environment.py +551 -551
- dtlpy/assets/__pycache__/__init__.cpython-310.pyc +0 -0
- dtlpy-1.115.44.data/scripts/dlp.bat +0 -2
- dtlpy-1.115.44.dist-info/RECORD +0 -240
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/entry_points.txt +0 -0
- {dtlpy-1.115.44.dist-info → dtlpy-1.116.6.dist-info}/top_level.txt +0 -0
dtlpy/services/events.py
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
import threading
|
|
2
|
-
import time
|
|
3
|
-
import traceback
|
|
4
|
-
import logging
|
|
5
|
-
import queue
|
|
6
|
-
import os
|
|
7
|
-
|
|
8
|
-
logger = logging.getLogger(name='dtlpy')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Events(threading.Thread):
|
|
12
|
-
def __init__(self, client_api, *args, **kwargs):
|
|
13
|
-
super(Events, self).__init__(*args, **kwargs)
|
|
14
|
-
self.client_api = client_api
|
|
15
|
-
self.q = queue.Queue()
|
|
16
|
-
self.mapping_events_dict = {
|
|
17
|
-
'project': {'method': ['create', 'delete'], 'route': '/projects'},
|
|
18
|
-
'task': {'method': ['create'], 'route': '/annotationtasks'},
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
def track(self, event):
|
|
22
|
-
try:
|
|
23
|
-
return_type, resp = self.client_api.gen_request(req_type='POST',
|
|
24
|
-
path='/analytics/metric/pendo',
|
|
25
|
-
json_req=event,
|
|
26
|
-
log_error=False)
|
|
27
|
-
if not resp.ok:
|
|
28
|
-
logger.debug('failed send event to analytics: {}'.format(resp.text))
|
|
29
|
-
except Exception:
|
|
30
|
-
logger.debug('failed send track event: {}'.format(traceback.format_exc()))
|
|
31
|
-
|
|
32
|
-
def run(self):
|
|
33
|
-
while True:
|
|
34
|
-
try:
|
|
35
|
-
event = self.q.get()
|
|
36
|
-
self.track([event])
|
|
37
|
-
self.q.task_done()
|
|
38
|
-
except Exception:
|
|
39
|
-
logger.exception('failed in loop')
|
|
40
|
-
|
|
41
|
-
def _valid_events(self, path):
|
|
42
|
-
for route in self.mapping_events_dict.values():
|
|
43
|
-
if path.startswith(route['route']) and 'sdk' not in path:
|
|
44
|
-
return True
|
|
45
|
-
return False
|
|
46
|
-
|
|
47
|
-
def _add_info(self, event_payload, function, resp):
|
|
48
|
-
if function in ['create']:
|
|
49
|
-
event_source = event_payload.get('event', None)
|
|
50
|
-
resp_json = resp.json()
|
|
51
|
-
if event_source == 'dtlpy:project':
|
|
52
|
-
event_payload['properties'].update({'project_id': resp_json['id'],
|
|
53
|
-
'project_name': resp_json['name']})
|
|
54
|
-
if event_source == 'dtlpy:task' and function in ['create']:
|
|
55
|
-
if 'createTaskPayload' in resp_json.get('spec', {}):
|
|
56
|
-
task_payload = resp_json.get('spec', {}).get('createTaskPayload', {})
|
|
57
|
-
else:
|
|
58
|
-
task_payload = resp_json
|
|
59
|
-
metadata = task_payload.get('metadata', {}).get('system', {})
|
|
60
|
-
task_type = task_payload.get('spec', {}).get('type', {})
|
|
61
|
-
allocation_method = 'Distribution'
|
|
62
|
-
if 'batchSize' in metadata and 'maxBatchWorkload' in metadata and 'allowedAssignees' in metadata:
|
|
63
|
-
allocation_method = 'Pulling'
|
|
64
|
-
event_payload['properties'].update({'task_type': task_type,
|
|
65
|
-
'allocation_method': allocation_method})
|
|
66
|
-
|
|
67
|
-
def put(self, event, resp=None, path=None):
|
|
68
|
-
send_event = True
|
|
69
|
-
if path is not None and not self._valid_events(path=path):
|
|
70
|
-
send_event = False
|
|
71
|
-
|
|
72
|
-
if resp is not None and send_event:
|
|
73
|
-
event_source = os.path.normpath(event.filename).split('\\')[-1][:-4]
|
|
74
|
-
event_payload = {'event': 'dtlpy:' + event_source,
|
|
75
|
-
'properties': {'sdk_event': event.function + '_' + event_source}}
|
|
76
|
-
if event_source in self.mapping_events_dict and \
|
|
77
|
-
event.function in self.mapping_events_dict[event_source]['method']:
|
|
78
|
-
self._add_info(event_payload=event_payload, function=event.function, resp=resp)
|
|
79
|
-
else:
|
|
80
|
-
send_event = False
|
|
81
|
-
else:
|
|
82
|
-
event_payload = event
|
|
83
|
-
if send_event:
|
|
84
|
-
self.q.put(event_payload)
|
|
1
|
+
import threading
|
|
2
|
+
import time
|
|
3
|
+
import traceback
|
|
4
|
+
import logging
|
|
5
|
+
import queue
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(name='dtlpy')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Events(threading.Thread):
|
|
12
|
+
def __init__(self, client_api, *args, **kwargs):
|
|
13
|
+
super(Events, self).__init__(*args, **kwargs)
|
|
14
|
+
self.client_api = client_api
|
|
15
|
+
self.q = queue.Queue()
|
|
16
|
+
self.mapping_events_dict = {
|
|
17
|
+
'project': {'method': ['create', 'delete'], 'route': '/projects'},
|
|
18
|
+
'task': {'method': ['create'], 'route': '/annotationtasks'},
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
def track(self, event):
|
|
22
|
+
try:
|
|
23
|
+
return_type, resp = self.client_api.gen_request(req_type='POST',
|
|
24
|
+
path='/analytics/metric/pendo',
|
|
25
|
+
json_req=event,
|
|
26
|
+
log_error=False)
|
|
27
|
+
if not resp.ok:
|
|
28
|
+
logger.debug('failed send event to analytics: {}'.format(resp.text))
|
|
29
|
+
except Exception:
|
|
30
|
+
logger.debug('failed send track event: {}'.format(traceback.format_exc()))
|
|
31
|
+
|
|
32
|
+
def run(self):
|
|
33
|
+
while True:
|
|
34
|
+
try:
|
|
35
|
+
event = self.q.get()
|
|
36
|
+
self.track([event])
|
|
37
|
+
self.q.task_done()
|
|
38
|
+
except Exception:
|
|
39
|
+
logger.exception('failed in loop')
|
|
40
|
+
|
|
41
|
+
def _valid_events(self, path):
|
|
42
|
+
for route in self.mapping_events_dict.values():
|
|
43
|
+
if path.startswith(route['route']) and 'sdk' not in path:
|
|
44
|
+
return True
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def _add_info(self, event_payload, function, resp):
|
|
48
|
+
if function in ['create']:
|
|
49
|
+
event_source = event_payload.get('event', None)
|
|
50
|
+
resp_json = resp.json()
|
|
51
|
+
if event_source == 'dtlpy:project':
|
|
52
|
+
event_payload['properties'].update({'project_id': resp_json['id'],
|
|
53
|
+
'project_name': resp_json['name']})
|
|
54
|
+
if event_source == 'dtlpy:task' and function in ['create']:
|
|
55
|
+
if 'createTaskPayload' in resp_json.get('spec', {}):
|
|
56
|
+
task_payload = resp_json.get('spec', {}).get('createTaskPayload', {})
|
|
57
|
+
else:
|
|
58
|
+
task_payload = resp_json
|
|
59
|
+
metadata = task_payload.get('metadata', {}).get('system', {})
|
|
60
|
+
task_type = task_payload.get('spec', {}).get('type', {})
|
|
61
|
+
allocation_method = 'Distribution'
|
|
62
|
+
if 'batchSize' in metadata and 'maxBatchWorkload' in metadata and 'allowedAssignees' in metadata:
|
|
63
|
+
allocation_method = 'Pulling'
|
|
64
|
+
event_payload['properties'].update({'task_type': task_type,
|
|
65
|
+
'allocation_method': allocation_method})
|
|
66
|
+
|
|
67
|
+
def put(self, event, resp=None, path=None):
|
|
68
|
+
send_event = True
|
|
69
|
+
if path is not None and not self._valid_events(path=path):
|
|
70
|
+
send_event = False
|
|
71
|
+
|
|
72
|
+
if resp is not None and send_event:
|
|
73
|
+
event_source = os.path.normpath(event.filename).split('\\')[-1][:-4]
|
|
74
|
+
event_payload = {'event': 'dtlpy:' + event_source,
|
|
75
|
+
'properties': {'sdk_event': event.function + '_' + event_source}}
|
|
76
|
+
if event_source in self.mapping_events_dict and \
|
|
77
|
+
event.function in self.mapping_events_dict[event_source]['method']:
|
|
78
|
+
self._add_info(event_payload=event_payload, function=event.function, resp=resp)
|
|
79
|
+
else:
|
|
80
|
+
send_event = False
|
|
81
|
+
else:
|
|
82
|
+
event_payload = event
|
|
83
|
+
if send_event:
|
|
84
|
+
self.q.put(event_payload)
|
dtlpy/services/logins.py
CHANGED
|
@@ -1,235 +1,235 @@
|
|
|
1
|
-
from urllib.parse import urlsplit, urlunsplit
|
|
2
|
-
import base64
|
|
3
|
-
import requests
|
|
4
|
-
import logging
|
|
5
|
-
import json
|
|
6
|
-
import jwt
|
|
7
|
-
import os
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(name='dtlpy')
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def login_m2m(api_client, email, password, client_id=None, client_secret=None, force=False):
|
|
13
|
-
return login_secret(api_client=api_client,
|
|
14
|
-
email=email,
|
|
15
|
-
password=password,
|
|
16
|
-
client_id=client_id,
|
|
17
|
-
client_secret=client_secret,
|
|
18
|
-
force=force)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def login_secret(api_client, email, password, client_id, client_secret=None, force=False):
|
|
22
|
-
"""
|
|
23
|
-
Login with email and password from environment variables
|
|
24
|
-
:param api_client: ApiClient instance
|
|
25
|
-
:param email: user email. if already logged in with same user - login will NOT happen. see "force"
|
|
26
|
-
:param password: user password
|
|
27
|
-
:param client_id: DEPRECATED
|
|
28
|
-
:param client_secret: DEPRECATED
|
|
29
|
-
:param force: force login. in case login with same user but want to get a new JWT
|
|
30
|
-
:return:
|
|
31
|
-
"""
|
|
32
|
-
# TODO add deprecation warning to client_id
|
|
33
|
-
# check if already logged in with SAME email
|
|
34
|
-
if api_client.token is not None or api_client.token == '':
|
|
35
|
-
try:
|
|
36
|
-
payload = jwt.decode(api_client.token, algorithms=['HS256'],
|
|
37
|
-
options={'verify_signature': False}, verify=False)
|
|
38
|
-
if 'email' in payload and \
|
|
39
|
-
payload['email'] == email and \
|
|
40
|
-
not api_client.token_expired() and \
|
|
41
|
-
not force:
|
|
42
|
-
return True
|
|
43
|
-
except jwt.exceptions.DecodeError:
|
|
44
|
-
logger.debug('{}'.format('Cant decode token. Force login is used'))
|
|
45
|
-
|
|
46
|
-
logger.info('[Start] Login Secret')
|
|
47
|
-
env_params = api_client.environments[api_client.environment]
|
|
48
|
-
# need to login
|
|
49
|
-
payload = {'username': email,
|
|
50
|
-
'password': password,
|
|
51
|
-
'type': 'user_credentials'
|
|
52
|
-
}
|
|
53
|
-
headers = {'content-type': 'application/json'}
|
|
54
|
-
if 'gate_url' not in env_params:
|
|
55
|
-
env_params['gate_url'] = gate_url_from_host(environment=api_client.environment)
|
|
56
|
-
api_client.environments[api_client.environment] = env_params
|
|
57
|
-
token_url = env_params['gate_url'] + "/token?default"
|
|
58
|
-
resp = requests.request("POST",
|
|
59
|
-
token_url,
|
|
60
|
-
data=json.dumps(payload),
|
|
61
|
-
headers=headers,
|
|
62
|
-
verify=env_params.get('verify_ssl', True))
|
|
63
|
-
if not resp.ok:
|
|
64
|
-
logout(api_client=api_client)
|
|
65
|
-
api_client.print_bad_response(resp)
|
|
66
|
-
return False
|
|
67
|
-
else:
|
|
68
|
-
response_dict = resp.json()
|
|
69
|
-
api_client.token = response_dict['id_token'] # this will also set the refresh_token to None
|
|
70
|
-
if 'refresh_token' in response_dict:
|
|
71
|
-
api_client.refresh_token = response_dict['refresh_token']
|
|
72
|
-
|
|
73
|
-
# set new client id for refresh
|
|
74
|
-
payload = jwt.decode(api_client.token, algorithms=['HS256'],
|
|
75
|
-
options={'verify_signature': False}, verify=False)
|
|
76
|
-
if 'email' in payload:
|
|
77
|
-
logger.info('[Done] Login Secret. User: {}'.format(payload['email']))
|
|
78
|
-
else:
|
|
79
|
-
logger.info('[Done] Login Secret. User: {}'.format(email))
|
|
80
|
-
logger.info(payload)
|
|
81
|
-
return True
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
def logout(api_client):
|
|
85
|
-
"""
|
|
86
|
-
remove JWT from cookie
|
|
87
|
-
"""
|
|
88
|
-
api_client.token = None
|
|
89
|
-
api_client.refresh_token = None
|
|
90
|
-
return True
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
def login_html():
|
|
94
|
-
try:
|
|
95
|
-
location = os.path.dirname(os.path.realpath(__file__))
|
|
96
|
-
except NameError:
|
|
97
|
-
location = './dtlpy/services'
|
|
98
|
-
filename = os.path.join(location, '..', 'assets', 'lock_open.png')
|
|
99
|
-
|
|
100
|
-
if os.path.isfile(filename):
|
|
101
|
-
|
|
102
|
-
with open(filename, 'rb') as f:
|
|
103
|
-
image = f.read()
|
|
104
|
-
|
|
105
|
-
html = (
|
|
106
|
-
" <!doctype html>\n"
|
|
107
|
-
" <html>\n"
|
|
108
|
-
" <head>\n"
|
|
109
|
-
" <style>\n"
|
|
110
|
-
" body {{\n"
|
|
111
|
-
" background-color: #F7F7F9 !important;\n"
|
|
112
|
-
" display: flex;\n"
|
|
113
|
-
" justify-content: center;\n"
|
|
114
|
-
" align-items: center;\n"
|
|
115
|
-
" height: 100vh;\n"
|
|
116
|
-
" width: 100vw;\n"
|
|
117
|
-
" margin: 0;\n"
|
|
118
|
-
" }}\n"
|
|
119
|
-
" img {{\n"
|
|
120
|
-
" display: block;\n"
|
|
121
|
-
" max-width: 100%;\n"
|
|
122
|
-
" max-height: 100%;\n"
|
|
123
|
-
" margin: auto;\n"
|
|
124
|
-
" }}\n"
|
|
125
|
-
" </style>\n"
|
|
126
|
-
" </head>\n"
|
|
127
|
-
" <body>\n"
|
|
128
|
-
" <img src='data:image/png;base64,{image}'>\n"
|
|
129
|
-
" </body>\n"
|
|
130
|
-
" </html>\n"
|
|
131
|
-
).format(image=base64.b64encode(image).decode())
|
|
132
|
-
else:
|
|
133
|
-
html = "<!doctype html><html><body>Logged in successfully</body></html>"
|
|
134
|
-
|
|
135
|
-
return html.encode('utf-8')
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
def login(api_client, auth0_url=None, audience=None, client_id=None, login_domain=None, callback_port=None):
|
|
139
|
-
import webbrowser
|
|
140
|
-
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
141
|
-
from urllib.parse import urlparse, parse_qs
|
|
142
|
-
logger.info('Logging in to Dataloop...')
|
|
143
|
-
|
|
144
|
-
class LocalServer:
|
|
145
|
-
|
|
146
|
-
class Handler(BaseHTTPRequestHandler):
|
|
147
|
-
|
|
148
|
-
tokens_obtained = False
|
|
149
|
-
id_token = None
|
|
150
|
-
access_token = None
|
|
151
|
-
refresh_token = None
|
|
152
|
-
|
|
153
|
-
def log_message(self, format, *args):
|
|
154
|
-
return
|
|
155
|
-
|
|
156
|
-
def do_GET(self):
|
|
157
|
-
parsed_path = urlparse(self.path)
|
|
158
|
-
query = parse_qs(parsed_path.query)
|
|
159
|
-
self.send_response(200)
|
|
160
|
-
self.send_header('Content-type', 'text/html')
|
|
161
|
-
self.end_headers()
|
|
162
|
-
self.wfile.write(login_html())
|
|
163
|
-
self.__class__.id_token = query['id_token'][0]
|
|
164
|
-
self.__class__.access_token = query['access_token'][0]
|
|
165
|
-
self.__class__.refresh_token = query['refresh_token'][0]
|
|
166
|
-
self.__class__.tokens_obtained = True
|
|
167
|
-
|
|
168
|
-
def __init__(self):
|
|
169
|
-
self.port = callback_port if callback_port is not None else 3001
|
|
170
|
-
self.server = HTTPServer(('', self.port), self.Handler)
|
|
171
|
-
self.server.timeout = 60
|
|
172
|
-
|
|
173
|
-
def process_request(self):
|
|
174
|
-
self.server.handle_request()
|
|
175
|
-
|
|
176
|
-
if self.Handler.tokens_obtained:
|
|
177
|
-
return True, {
|
|
178
|
-
"id": self.Handler.id_token,
|
|
179
|
-
"access": self.Handler.access_token,
|
|
180
|
-
"refresh": self.Handler.refresh_token
|
|
181
|
-
}
|
|
182
|
-
else:
|
|
183
|
-
return False, {}
|
|
184
|
-
|
|
185
|
-
def local_endpoint(self):
|
|
186
|
-
return "http://localhost:{}".format(self.port)
|
|
187
|
-
|
|
188
|
-
def close(self):
|
|
189
|
-
self.server.server_close()
|
|
190
|
-
|
|
191
|
-
server = LocalServer()
|
|
192
|
-
|
|
193
|
-
try:
|
|
194
|
-
local_ep = server.local_endpoint()
|
|
195
|
-
env_params = api_client.environments[api_client.environment]
|
|
196
|
-
if 'gate_url' not in env_params:
|
|
197
|
-
env_params['gate_url'] = gate_url_from_host(environment=api_client.environment)
|
|
198
|
-
api_client.environments[api_client.environment] = env_params
|
|
199
|
-
remote_ep = env_params['gate_url']
|
|
200
|
-
login_page_url = "{}/login?callback={}".format(remote_ep, local_ep)
|
|
201
|
-
if login_domain is not None:
|
|
202
|
-
login_page_url = "{}&domain={}".format(login_page_url, login_domain)
|
|
203
|
-
logger.info("Launching interactive login via {}".format(remote_ep))
|
|
204
|
-
webbrowser.open(url=login_page_url, new=2, autoraise=True)
|
|
205
|
-
|
|
206
|
-
success, tokens = server.process_request()
|
|
207
|
-
|
|
208
|
-
if success:
|
|
209
|
-
decoded_jwt = jwt.decode(tokens['id'], verify=False,
|
|
210
|
-
options={'verify_signature': False})
|
|
211
|
-
|
|
212
|
-
if 'email' in decoded_jwt:
|
|
213
|
-
logger.info('Logged in: {}'.format(decoded_jwt['email']))
|
|
214
|
-
else:
|
|
215
|
-
logger.info('Logged in: unknown user')
|
|
216
|
-
|
|
217
|
-
api_client.token = tokens['id']
|
|
218
|
-
api_client.refresh_token = tokens['refresh']
|
|
219
|
-
|
|
220
|
-
return True
|
|
221
|
-
else:
|
|
222
|
-
logout(api_client=api_client)
|
|
223
|
-
logger.error('Login failed: no tokens obtained')
|
|
224
|
-
return False
|
|
225
|
-
except Exception as err:
|
|
226
|
-
logout(api_client=api_client)
|
|
227
|
-
logger.exception('Login failed: error while getting token', err)
|
|
228
|
-
return False
|
|
229
|
-
finally:
|
|
230
|
-
server.close()
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
def gate_url_from_host(environment):
|
|
234
|
-
parsed = urlsplit(environment)
|
|
235
|
-
return urlunsplit((parsed.scheme, parsed.netloc, '', '', ''))
|
|
1
|
+
from urllib.parse import urlsplit, urlunsplit
|
|
2
|
+
import base64
|
|
3
|
+
import requests
|
|
4
|
+
import logging
|
|
5
|
+
import json
|
|
6
|
+
import jwt
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(name='dtlpy')
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def login_m2m(api_client, email, password, client_id=None, client_secret=None, force=False):
|
|
13
|
+
return login_secret(api_client=api_client,
|
|
14
|
+
email=email,
|
|
15
|
+
password=password,
|
|
16
|
+
client_id=client_id,
|
|
17
|
+
client_secret=client_secret,
|
|
18
|
+
force=force)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def login_secret(api_client, email, password, client_id, client_secret=None, force=False):
|
|
22
|
+
"""
|
|
23
|
+
Login with email and password from environment variables
|
|
24
|
+
:param api_client: ApiClient instance
|
|
25
|
+
:param email: user email. if already logged in with same user - login will NOT happen. see "force"
|
|
26
|
+
:param password: user password
|
|
27
|
+
:param client_id: DEPRECATED
|
|
28
|
+
:param client_secret: DEPRECATED
|
|
29
|
+
:param force: force login. in case login with same user but want to get a new JWT
|
|
30
|
+
:return:
|
|
31
|
+
"""
|
|
32
|
+
# TODO add deprecation warning to client_id
|
|
33
|
+
# check if already logged in with SAME email
|
|
34
|
+
if api_client.token is not None or api_client.token == '':
|
|
35
|
+
try:
|
|
36
|
+
payload = jwt.decode(api_client.token, algorithms=['HS256'],
|
|
37
|
+
options={'verify_signature': False}, verify=False)
|
|
38
|
+
if 'email' in payload and \
|
|
39
|
+
payload['email'] == email and \
|
|
40
|
+
not api_client.token_expired() and \
|
|
41
|
+
not force:
|
|
42
|
+
return True
|
|
43
|
+
except jwt.exceptions.DecodeError:
|
|
44
|
+
logger.debug('{}'.format('Cant decode token. Force login is used'))
|
|
45
|
+
|
|
46
|
+
logger.info('[Start] Login Secret')
|
|
47
|
+
env_params = api_client.environments[api_client.environment]
|
|
48
|
+
# need to login
|
|
49
|
+
payload = {'username': email,
|
|
50
|
+
'password': password,
|
|
51
|
+
'type': 'user_credentials'
|
|
52
|
+
}
|
|
53
|
+
headers = {'content-type': 'application/json'}
|
|
54
|
+
if 'gate_url' not in env_params:
|
|
55
|
+
env_params['gate_url'] = gate_url_from_host(environment=api_client.environment)
|
|
56
|
+
api_client.environments[api_client.environment] = env_params
|
|
57
|
+
token_url = env_params['gate_url'] + "/token?default"
|
|
58
|
+
resp = requests.request("POST",
|
|
59
|
+
token_url,
|
|
60
|
+
data=json.dumps(payload),
|
|
61
|
+
headers=headers,
|
|
62
|
+
verify=env_params.get('verify_ssl', True))
|
|
63
|
+
if not resp.ok:
|
|
64
|
+
logout(api_client=api_client)
|
|
65
|
+
api_client.print_bad_response(resp)
|
|
66
|
+
return False
|
|
67
|
+
else:
|
|
68
|
+
response_dict = resp.json()
|
|
69
|
+
api_client.token = response_dict['id_token'] # this will also set the refresh_token to None
|
|
70
|
+
if 'refresh_token' in response_dict:
|
|
71
|
+
api_client.refresh_token = response_dict['refresh_token']
|
|
72
|
+
|
|
73
|
+
# set new client id for refresh
|
|
74
|
+
payload = jwt.decode(api_client.token, algorithms=['HS256'],
|
|
75
|
+
options={'verify_signature': False}, verify=False)
|
|
76
|
+
if 'email' in payload:
|
|
77
|
+
logger.info('[Done] Login Secret. User: {}'.format(payload['email']))
|
|
78
|
+
else:
|
|
79
|
+
logger.info('[Done] Login Secret. User: {}'.format(email))
|
|
80
|
+
logger.info(payload)
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def logout(api_client):
|
|
85
|
+
"""
|
|
86
|
+
remove JWT from cookie
|
|
87
|
+
"""
|
|
88
|
+
api_client.token = None
|
|
89
|
+
api_client.refresh_token = None
|
|
90
|
+
return True
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def login_html():
|
|
94
|
+
try:
|
|
95
|
+
location = os.path.dirname(os.path.realpath(__file__))
|
|
96
|
+
except NameError:
|
|
97
|
+
location = './dtlpy/services'
|
|
98
|
+
filename = os.path.join(location, '..', 'assets', 'lock_open.png')
|
|
99
|
+
|
|
100
|
+
if os.path.isfile(filename):
|
|
101
|
+
|
|
102
|
+
with open(filename, 'rb') as f:
|
|
103
|
+
image = f.read()
|
|
104
|
+
|
|
105
|
+
html = (
|
|
106
|
+
" <!doctype html>\n"
|
|
107
|
+
" <html>\n"
|
|
108
|
+
" <head>\n"
|
|
109
|
+
" <style>\n"
|
|
110
|
+
" body {{\n"
|
|
111
|
+
" background-color: #F7F7F9 !important;\n"
|
|
112
|
+
" display: flex;\n"
|
|
113
|
+
" justify-content: center;\n"
|
|
114
|
+
" align-items: center;\n"
|
|
115
|
+
" height: 100vh;\n"
|
|
116
|
+
" width: 100vw;\n"
|
|
117
|
+
" margin: 0;\n"
|
|
118
|
+
" }}\n"
|
|
119
|
+
" img {{\n"
|
|
120
|
+
" display: block;\n"
|
|
121
|
+
" max-width: 100%;\n"
|
|
122
|
+
" max-height: 100%;\n"
|
|
123
|
+
" margin: auto;\n"
|
|
124
|
+
" }}\n"
|
|
125
|
+
" </style>\n"
|
|
126
|
+
" </head>\n"
|
|
127
|
+
" <body>\n"
|
|
128
|
+
" <img src='data:image/png;base64,{image}'>\n"
|
|
129
|
+
" </body>\n"
|
|
130
|
+
" </html>\n"
|
|
131
|
+
).format(image=base64.b64encode(image).decode())
|
|
132
|
+
else:
|
|
133
|
+
html = "<!doctype html><html><body>Logged in successfully</body></html>"
|
|
134
|
+
|
|
135
|
+
return html.encode('utf-8')
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def login(api_client, auth0_url=None, audience=None, client_id=None, login_domain=None, callback_port=None):
|
|
139
|
+
import webbrowser
|
|
140
|
+
from http.server import BaseHTTPRequestHandler, HTTPServer
|
|
141
|
+
from urllib.parse import urlparse, parse_qs
|
|
142
|
+
logger.info('Logging in to Dataloop...')
|
|
143
|
+
|
|
144
|
+
class LocalServer:
|
|
145
|
+
|
|
146
|
+
class Handler(BaseHTTPRequestHandler):
|
|
147
|
+
|
|
148
|
+
tokens_obtained = False
|
|
149
|
+
id_token = None
|
|
150
|
+
access_token = None
|
|
151
|
+
refresh_token = None
|
|
152
|
+
|
|
153
|
+
def log_message(self, format, *args):
|
|
154
|
+
return
|
|
155
|
+
|
|
156
|
+
def do_GET(self):
|
|
157
|
+
parsed_path = urlparse(self.path)
|
|
158
|
+
query = parse_qs(parsed_path.query)
|
|
159
|
+
self.send_response(200)
|
|
160
|
+
self.send_header('Content-type', 'text/html')
|
|
161
|
+
self.end_headers()
|
|
162
|
+
self.wfile.write(login_html())
|
|
163
|
+
self.__class__.id_token = query['id_token'][0]
|
|
164
|
+
self.__class__.access_token = query['access_token'][0]
|
|
165
|
+
self.__class__.refresh_token = query['refresh_token'][0]
|
|
166
|
+
self.__class__.tokens_obtained = True
|
|
167
|
+
|
|
168
|
+
def __init__(self):
|
|
169
|
+
self.port = callback_port if callback_port is not None else 3001
|
|
170
|
+
self.server = HTTPServer(('', self.port), self.Handler)
|
|
171
|
+
self.server.timeout = 60
|
|
172
|
+
|
|
173
|
+
def process_request(self):
|
|
174
|
+
self.server.handle_request()
|
|
175
|
+
|
|
176
|
+
if self.Handler.tokens_obtained:
|
|
177
|
+
return True, {
|
|
178
|
+
"id": self.Handler.id_token,
|
|
179
|
+
"access": self.Handler.access_token,
|
|
180
|
+
"refresh": self.Handler.refresh_token
|
|
181
|
+
}
|
|
182
|
+
else:
|
|
183
|
+
return False, {}
|
|
184
|
+
|
|
185
|
+
def local_endpoint(self):
|
|
186
|
+
return "http://localhost:{}".format(self.port)
|
|
187
|
+
|
|
188
|
+
def close(self):
|
|
189
|
+
self.server.server_close()
|
|
190
|
+
|
|
191
|
+
server = LocalServer()
|
|
192
|
+
|
|
193
|
+
try:
|
|
194
|
+
local_ep = server.local_endpoint()
|
|
195
|
+
env_params = api_client.environments[api_client.environment]
|
|
196
|
+
if 'gate_url' not in env_params:
|
|
197
|
+
env_params['gate_url'] = gate_url_from_host(environment=api_client.environment)
|
|
198
|
+
api_client.environments[api_client.environment] = env_params
|
|
199
|
+
remote_ep = env_params['gate_url']
|
|
200
|
+
login_page_url = "{}/login?callback={}".format(remote_ep, local_ep)
|
|
201
|
+
if login_domain is not None:
|
|
202
|
+
login_page_url = "{}&domain={}".format(login_page_url, login_domain)
|
|
203
|
+
logger.info("Launching interactive login via {}".format(remote_ep))
|
|
204
|
+
webbrowser.open(url=login_page_url, new=2, autoraise=True)
|
|
205
|
+
|
|
206
|
+
success, tokens = server.process_request()
|
|
207
|
+
|
|
208
|
+
if success:
|
|
209
|
+
decoded_jwt = jwt.decode(tokens['id'], verify=False,
|
|
210
|
+
options={'verify_signature': False})
|
|
211
|
+
|
|
212
|
+
if 'email' in decoded_jwt:
|
|
213
|
+
logger.info('Logged in: {}'.format(decoded_jwt['email']))
|
|
214
|
+
else:
|
|
215
|
+
logger.info('Logged in: unknown user')
|
|
216
|
+
|
|
217
|
+
api_client.token = tokens['id']
|
|
218
|
+
api_client.refresh_token = tokens['refresh']
|
|
219
|
+
|
|
220
|
+
return True
|
|
221
|
+
else:
|
|
222
|
+
logout(api_client=api_client)
|
|
223
|
+
logger.error('Login failed: no tokens obtained')
|
|
224
|
+
return False
|
|
225
|
+
except Exception as err:
|
|
226
|
+
logout(api_client=api_client)
|
|
227
|
+
logger.exception('Login failed: error while getting token', err)
|
|
228
|
+
return False
|
|
229
|
+
finally:
|
|
230
|
+
server.close()
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def gate_url_from_host(environment):
|
|
234
|
+
parsed = urlsplit(environment)
|
|
235
|
+
return urlunsplit((parsed.scheme, parsed.netloc, '', '', ''))
|