t2d2-sdk 1.2.dev5__tar.gz → 1.4.0__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.
- {t2d2-sdk-1.2.dev5/src/t2d2_sdk.egg-info → t2d2-sdk-1.4.0}/PKG-INFO +2 -1
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/README.md +1 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/_version.py +2 -2
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0/src/t2d2_sdk.egg-info}/PKG-INFO +2 -1
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/t2d2_sdk.py +97 -31
- t2d2-sdk-1.4.0/tests/t2d2_test.py +101 -0
- t2d2-sdk-1.2.dev5/tests/t2d2_test.py +0 -38
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/.github/workflows/publish_pypi.yaml +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/.gitignore +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/LICENSE +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/pyproject.toml +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/setup.cfg +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/t2d2_sdk.egg-info/SOURCES.txt +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/t2d2_sdk.egg-info/dependency_links.txt +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/t2d2_sdk.egg-info/requires.txt +0 -0
- {t2d2-sdk-1.2.dev5 → t2d2-sdk-1.4.0}/src/t2d2_sdk.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: t2d2-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: T2D2 SDK
|
|
5
5
|
Author-email: Badri Hiriyur <badri@t2d2.ai>
|
|
6
6
|
Project-URL: Homepage, https://t2d2.ai
|
|
@@ -54,6 +54,7 @@ With this client, you can interface with the app and get access to most of the f
|
|
|
54
54
|
|
|
55
55
|
```python
|
|
56
56
|
t2d2.set_project(PROJECT_ID)
|
|
57
|
+
project = t2d2.get_project()
|
|
57
58
|
data = t2d2.get_images()
|
|
58
59
|
...
|
|
59
60
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: t2d2-sdk
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: T2D2 SDK
|
|
5
5
|
Author-email: Badri Hiriyur <badri@t2d2.ai>
|
|
6
6
|
Project-URL: Homepage, https://t2d2.ai
|
|
@@ -54,6 +54,7 @@ With this client, you can interface with the app and get access to most of the f
|
|
|
54
54
|
|
|
55
55
|
```python
|
|
56
56
|
t2d2.set_project(PROJECT_ID)
|
|
57
|
+
project = t2d2.get_project()
|
|
57
58
|
data = t2d2.get_images()
|
|
58
59
|
...
|
|
59
60
|
```
|
|
@@ -12,6 +12,7 @@ TIMEOUT = 60
|
|
|
12
12
|
BASE_URL = os.getenv("T2D2_API_URL", "https://api-v3.t2d2.ai/api/")
|
|
13
13
|
# DEV https://api-v3-dev.t2d2.ai/api/
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
####################################################################################################
|
|
16
17
|
def random_string(length: int = 6) -> str:
|
|
17
18
|
"""Generate a random string of fixed length"""
|
|
@@ -30,7 +31,10 @@ def download_file(url: str, file_path: str):
|
|
|
30
31
|
s3.download_file(bucket, key, file_path)
|
|
31
32
|
return {"success": True, "message": "File downloaded"}
|
|
32
33
|
except Exception as e:
|
|
33
|
-
return {
|
|
34
|
+
return {
|
|
35
|
+
"success": False,
|
|
36
|
+
"message": f"{str(e)} \n{bucket} \n{key} \n{file_path}",
|
|
37
|
+
}
|
|
34
38
|
|
|
35
39
|
|
|
36
40
|
def upload_file(file_path: str, url: str):
|
|
@@ -43,7 +47,10 @@ def upload_file(file_path: str, url: str):
|
|
|
43
47
|
s3.upload_file(file_path, bucket, key, ExtraArgs={"ACL": "public-read"})
|
|
44
48
|
return {"success": True, "message": "File uploaded"}
|
|
45
49
|
except Exception as e:
|
|
46
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
"success": False,
|
|
52
|
+
"message": f"{str(e)} \n{bucket} \n{key} \n{file_path}",
|
|
53
|
+
}
|
|
47
54
|
|
|
48
55
|
|
|
49
56
|
####################################################################################################
|
|
@@ -202,12 +209,17 @@ class T2D2(object):
|
|
|
202
209
|
|
|
203
210
|
def get_images(self, image_ids=None, params=None):
|
|
204
211
|
"""Return image list based on specified ids"""
|
|
205
|
-
if image_ids is None:
|
|
206
|
-
return []
|
|
207
|
-
|
|
208
212
|
if not self.project:
|
|
209
|
-
raise ValueError("Project not set")
|
|
213
|
+
raise ValueError("Project not set yet")
|
|
214
|
+
|
|
215
|
+
# all images in project
|
|
216
|
+
if image_ids is None:
|
|
217
|
+
url = f"{self.project['id']}/images"
|
|
218
|
+
json_data = self.request(url, RequestType.GET, params=params)
|
|
219
|
+
results = json_data["data"]["image_list"]
|
|
220
|
+
return results
|
|
210
221
|
|
|
222
|
+
# Specified image_ids
|
|
211
223
|
results = []
|
|
212
224
|
for img_id in image_ids:
|
|
213
225
|
url = f"{self.project['id']}/images/{img_id}"
|
|
@@ -260,32 +272,9 @@ class T2D2(object):
|
|
|
260
272
|
results.append(json_data["data"])
|
|
261
273
|
return results
|
|
262
274
|
|
|
263
|
-
def get_geotags(self, drawing_id=None, params=None):
|
|
264
|
-
"""Return annotation list based on specified ids"""
|
|
265
|
-
if drawing_id is None:
|
|
266
|
-
return []
|
|
267
|
-
|
|
268
|
-
if not self.project:
|
|
269
|
-
raise ValueError("Project not set")
|
|
270
|
-
|
|
271
|
-
url = f"{self.project['id']}/geotags?drawing_id={drawing_id}"
|
|
272
|
-
json_data = self.request(url, RequestType.GET, params=params)
|
|
273
|
-
|
|
274
|
-
return json_data["data"]
|
|
275
|
-
|
|
276
275
|
################################################################################################
|
|
277
|
-
# Add / Upload methods
|
|
276
|
+
# Add / Upload Asset methods
|
|
278
277
|
################################################################################################
|
|
279
|
-
def add_annotations(self, payloads):
|
|
280
|
-
"""Add annotations"""
|
|
281
|
-
results = []
|
|
282
|
-
for payload in payloads:
|
|
283
|
-
url = "annotation"
|
|
284
|
-
res = self.request(url, RequestType.POST, data=payload)
|
|
285
|
-
results.append(res)
|
|
286
|
-
|
|
287
|
-
return results
|
|
288
|
-
|
|
289
278
|
def add_assets(self, payload):
|
|
290
279
|
"""Add assets"""
|
|
291
280
|
url = f"{self.project['id']}/assets/bulk.create"
|
|
@@ -400,7 +389,10 @@ class T2D2(object):
|
|
|
400
389
|
s3_path = (
|
|
401
390
|
self.s3_base_url + f"/projects/{self.project['id']}/reports/{filename}"
|
|
402
391
|
)
|
|
403
|
-
upload_file(file_path, s3_path)
|
|
392
|
+
response = upload_file(file_path, s3_path)
|
|
393
|
+
if not response["success"]:
|
|
394
|
+
raise ValueError(response["message"])
|
|
395
|
+
|
|
404
396
|
assets.append(
|
|
405
397
|
{
|
|
406
398
|
"name": base,
|
|
@@ -416,3 +408,77 @@ class T2D2(object):
|
|
|
416
408
|
res = self.request(url, RequestType.POST, data=payload)
|
|
417
409
|
|
|
418
410
|
return res
|
|
411
|
+
|
|
412
|
+
################################################################################################
|
|
413
|
+
# Annotation methods
|
|
414
|
+
################################################################################################
|
|
415
|
+
def get_annotations(self, image_id=None, params=None):
|
|
416
|
+
"""TODO: Return annotation list based on specified ids"""
|
|
417
|
+
if not self.project:
|
|
418
|
+
raise ValueError("Project not set")
|
|
419
|
+
|
|
420
|
+
if params is None:
|
|
421
|
+
params = {}
|
|
422
|
+
|
|
423
|
+
if image_id is None:
|
|
424
|
+
images = self.get_images(params=params)
|
|
425
|
+
image_ids = [img["id"] for img in images]
|
|
426
|
+
else:
|
|
427
|
+
image_ids = [image_id]
|
|
428
|
+
|
|
429
|
+
images = self.get_images(image_ids=image_ids, params=params)
|
|
430
|
+
annotations = []
|
|
431
|
+
for img in images:
|
|
432
|
+
annotations.extend(img["annotations"])
|
|
433
|
+
|
|
434
|
+
return annotations
|
|
435
|
+
|
|
436
|
+
def delete_annotations(self, image_id, annotation_ids=None):
|
|
437
|
+
"""Delete annotations"""
|
|
438
|
+
|
|
439
|
+
if not self.project:
|
|
440
|
+
raise ValueError("Project not set")
|
|
441
|
+
|
|
442
|
+
if annotation_ids is None:
|
|
443
|
+
# Get annotation_ids for all annotations in image
|
|
444
|
+
annotations = self.get_annotations(image_id)
|
|
445
|
+
annotation_ids = [ann["id"] for ann in annotations]
|
|
446
|
+
|
|
447
|
+
payload = {
|
|
448
|
+
"project_id": self.project["id"],
|
|
449
|
+
"image_id": image_id,
|
|
450
|
+
"annotation_ids": annotation_ids,
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
return self.request("annotation", RequestType.DELETE, data=payload)
|
|
454
|
+
|
|
455
|
+
def add_annotations(self, image_id, annotations):
|
|
456
|
+
"""TODO: Add annotations"""
|
|
457
|
+
if not self.project:
|
|
458
|
+
raise ValueError("Project not set")
|
|
459
|
+
|
|
460
|
+
url = "annotation"
|
|
461
|
+
payload = {
|
|
462
|
+
"project_id": self.project["id"],
|
|
463
|
+
"image_id": image_id,
|
|
464
|
+
"annotations": annotations,
|
|
465
|
+
}
|
|
466
|
+
results = self.request(url, RequestType.POST, data=payload)
|
|
467
|
+
|
|
468
|
+
return results
|
|
469
|
+
|
|
470
|
+
################################################################################################
|
|
471
|
+
# Geotag methods
|
|
472
|
+
################################################################################################
|
|
473
|
+
def get_geotags(self, drawing_id, params=None):
|
|
474
|
+
"""Return annotation list based on specified ids"""
|
|
475
|
+
if drawing_id is None:
|
|
476
|
+
return []
|
|
477
|
+
|
|
478
|
+
if not self.project:
|
|
479
|
+
raise ValueError("Project not set")
|
|
480
|
+
|
|
481
|
+
url = f"{self.project['id']}/geotags?drawing_id={drawing_id}"
|
|
482
|
+
json_data = self.request(url, RequestType.GET, params=params)
|
|
483
|
+
|
|
484
|
+
return json_data["data"]
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# pylint: disable=missing-docstring,line-too-long
|
|
2
|
+
import os
|
|
3
|
+
import random
|
|
4
|
+
import sys
|
|
5
|
+
import unittest
|
|
6
|
+
|
|
7
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), "../src"))
|
|
8
|
+
|
|
9
|
+
# pylint: disable=import-error, wrong-import-position
|
|
10
|
+
from t2d2_sdk import T2D2, random_string
|
|
11
|
+
|
|
12
|
+
creds_api_key = {"api_key": os.getenv("T2D2_API_KEY", "")}
|
|
13
|
+
BASE_URL = "https://api-v3.t2d2.ai/api"
|
|
14
|
+
PROJECT_ID = 44
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def gen_random_polygons(n=5):
|
|
18
|
+
"""Return random polygon points"""
|
|
19
|
+
points = []
|
|
20
|
+
for _ in range(n):
|
|
21
|
+
points.append([random.random(), random.random()])
|
|
22
|
+
return points
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def gen_random_box():
|
|
26
|
+
"""Return random box points"""
|
|
27
|
+
points = [
|
|
28
|
+
0.1 + 0.01 * random.random(),
|
|
29
|
+
0.2 + 0.01 * random.random(),
|
|
30
|
+
0.4 + 0.01 * random.random(),
|
|
31
|
+
0.8 + 0.01 * random.random(),
|
|
32
|
+
]
|
|
33
|
+
return points
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def generate_random_annotations(n=10):
|
|
37
|
+
"""Generate random annotations"""
|
|
38
|
+
annotations = []
|
|
39
|
+
for _ in range(n):
|
|
40
|
+
annotations.append(
|
|
41
|
+
{
|
|
42
|
+
# "points": gen_random_polygons(),
|
|
43
|
+
# "shape": 4,
|
|
44
|
+
"points": gen_random_box(),
|
|
45
|
+
"shape": 3,
|
|
46
|
+
"annotation_class_name": "efflorescence",
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return annotations
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class T2D2APITests(unittest.TestCase):
|
|
54
|
+
def setUp(self):
|
|
55
|
+
# Initialize T2D2 object with dummy credentials
|
|
56
|
+
self.t2d2 = T2D2(credentials=creds_api_key, base_url=BASE_URL)
|
|
57
|
+
self.t2d2.set_project(PROJECT_ID)
|
|
58
|
+
|
|
59
|
+
def test_random_string(self):
|
|
60
|
+
# Test random_string function
|
|
61
|
+
result = random_string(length=6)
|
|
62
|
+
self.assertEqual(len(result), 6)
|
|
63
|
+
|
|
64
|
+
def test_project(self):
|
|
65
|
+
# Test project function
|
|
66
|
+
project = self.t2d2.get_project(PROJECT_ID)
|
|
67
|
+
self.assertEqual(project["id"], PROJECT_ID)
|
|
68
|
+
|
|
69
|
+
def test_images(self):
|
|
70
|
+
# Get Images
|
|
71
|
+
images = self.t2d2.get_images(params={"image_types": [1, 2, 4]})
|
|
72
|
+
self.assertEqual(len(images), 205)
|
|
73
|
+
|
|
74
|
+
# Get Images
|
|
75
|
+
orthos = self.t2d2.get_images(params={"image_types": [3]})
|
|
76
|
+
self.assertEqual(len(orthos), 12)
|
|
77
|
+
|
|
78
|
+
def test_annotations(self):
|
|
79
|
+
# Test annotations function
|
|
80
|
+
image_id = 330243
|
|
81
|
+
params = {}
|
|
82
|
+
|
|
83
|
+
res = self.t2d2.delete_annotations(image_id=330243)
|
|
84
|
+
self.assertEqual(res["success"], True)
|
|
85
|
+
|
|
86
|
+
annotations = self.t2d2.get_annotations(image_id=image_id, params=params)
|
|
87
|
+
self.assertEqual(len(annotations), 0)
|
|
88
|
+
|
|
89
|
+
anns = generate_random_annotations(n=10)
|
|
90
|
+
# print(anns)
|
|
91
|
+
res = self.t2d2.add_annotations(image_id=image_id, annotations=anns)
|
|
92
|
+
self.assertEqual(res["success"], True)
|
|
93
|
+
# print(res)
|
|
94
|
+
|
|
95
|
+
annotations = self.t2d2.get_annotations(image_id=330243, params=params)
|
|
96
|
+
self.assertEqual(len(annotations), 10)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
if __name__ == "__main__":
|
|
100
|
+
print("Running tests...")
|
|
101
|
+
unittest.main()
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
# pylint: disable=missing-docstring,line-too-long
|
|
2
|
-
import os
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from t2d2_sdk import T2D2, random_string
|
|
6
|
-
|
|
7
|
-
creds_api_key = {
|
|
8
|
-
"api_key": os.getenv("T2D2_API_KEY", ""),
|
|
9
|
-
}
|
|
10
|
-
PROJECT_ID = 1
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class T2D2APITests(unittest.TestCase):
|
|
14
|
-
def setUp(self):
|
|
15
|
-
# Initialize T2D2 object with dummy credentials
|
|
16
|
-
self.t2d2 = T2D2(
|
|
17
|
-
credentials=creds_api_key, base_url="http://localhost:4000/api/"
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
def test_random_string(self):
|
|
21
|
-
# Test random_string function
|
|
22
|
-
result = random_string(length=6)
|
|
23
|
-
self.assertEqual(len(result), 6)
|
|
24
|
-
|
|
25
|
-
def test_project(self):
|
|
26
|
-
# Test project function
|
|
27
|
-
project = self.t2d2.get_project(PROJECT_ID)
|
|
28
|
-
self.assertEqual(project["id"], PROJECT_ID)
|
|
29
|
-
|
|
30
|
-
self.t2d2.set_project(PROJECT_ID)
|
|
31
|
-
self.assertEqual(self.t2d2.project["id"], PROJECT_ID)
|
|
32
|
-
|
|
33
|
-
# Add more test cases for other functions...
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if __name__ == "__main__":
|
|
37
|
-
print("Running tests...")
|
|
38
|
-
unittest.main()
|
|
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
|