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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t2d2-sdk
3
- Version: 1.2.dev5
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
  ```
@@ -38,6 +38,7 @@ With this client, you can interface with the app and get access to most of the f
38
38
 
39
39
  ```python
40
40
  t2d2.set_project(PROJECT_ID)
41
+ project = t2d2.get_project()
41
42
  data = t2d2.get_images()
42
43
  ...
43
44
  ```
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '1.2.dev5'
16
- __version_tuple__ = version_tuple = (1, 2, 'dev5')
15
+ __version__ = version = '1.4.0'
16
+ __version_tuple__ = version_tuple = (1, 4, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: t2d2-sdk
3
- Version: 1.2.dev5
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 {"success": False, "message": f"{str(e)} \n{bucket} \n{key} \n{file_path}"}
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 {"success": False, "message": f"{str(e)} \n{bucket} \n{key} \n{file_path}"}
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