annolab 0.3.2__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.
annolab/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+
2
+ # Configuration
3
+ api_key = None
4
+
5
+ from annolab.annolab import AnnoLab
6
+ from annolab.project import Project
annolab/annolab.py ADDED
@@ -0,0 +1,76 @@
1
+ from annolab.project_import import ProjectImport
2
+ import os
3
+
4
+ from annolab import endpoints
5
+ from annolab.project import Project
6
+ from annolab.api_helper import ApiHelper
7
+
8
+ class AnnoLab:
9
+
10
+ def __init__(
11
+ self,
12
+ api_key = None,
13
+ api_url = 'https://api.annolab.ai',
14
+ ):
15
+ self.__api = ApiHelper(api_key=api_key, api_url=api_url)
16
+
17
+ @property
18
+ def api_key_info(self):
19
+ return self.__api.api_key_info
20
+
21
+
22
+ @property
23
+ def default_owner(self):
24
+ """
25
+ Returns the default group to use for the api key.
26
+ The default group is the group representing the single user.
27
+ """
28
+ return self.__api.default_owner
29
+
30
+
31
+ def find_project(self, name: str, owner_name: str = None):
32
+ """
33
+ Find a project by name and (optionally) group name.
34
+ If group name is not passed, the user's default group is used.
35
+ """
36
+ owner_name = owner_name or self.default_owner['groupName']
37
+
38
+ res = self.__api.get_request(
39
+ endpoints.Project.get_group_project(owner_name, name)
40
+ )
41
+
42
+ return Project.create_from_response_json(res.json(), self.__api)
43
+
44
+
45
+ def create_project(self, name: str, owner_name: str = None, is_public = False):
46
+ """
47
+ Create a project.
48
+ If group name is not passed, the user's default group is used.
49
+ """
50
+ owner_name = owner_name or self.default_owner['groupName']
51
+
52
+ res = self.__api.post_request(
53
+ endpoints.Project.post_create(),
54
+ {
55
+ 'name': name,
56
+ 'groupName': owner_name,
57
+ 'isPublic': is_public
58
+ }
59
+ )
60
+
61
+ return Project.create_from_response_json(res.json(), self.__api)
62
+
63
+
64
+ def create_project_from_export(self, filepath: str, name: str = None, owner_name: str = None, is_public=False):
65
+ if (name is None):
66
+ name = os.path.basename(filepath).split('.')[0]
67
+
68
+ project = self.create_project(name, owner_name, is_public=is_public)
69
+ project_import = ProjectImport(filepath, project, owner_name)
70
+
71
+ project_import.unzip_export()
72
+ project_import.import_all()
73
+ project_import.cleanup()
74
+
75
+ return project
76
+
annolab/annotation.py ADDED
@@ -0,0 +1,37 @@
1
+ from typing import Dict
2
+
3
+ class Annotation:
4
+
5
+ @staticmethod
6
+ def create_api_annotation(dict: Dict):
7
+ """
8
+ Maps an sdk annotation dict to an api annotation dict, for use in calls to the api.
9
+
10
+ Annotation parameters:
11
+ type: str (Required)
12
+ client_id: str (Optional, Required if passing relations)
13
+ value: str (Optional)
14
+ offsets: [int, int] (Optional)
15
+ text_bounds { 'type': 'Polygon', coordinates: List[List[List[int]]] } (Optional)
16
+ bbox: [int, int, int, int] (Optional, Ignored if text_bounds are passed)
17
+ layer: str (Optional)
18
+ page: int (Optional)
19
+ reviewed bool (Optional)
20
+ """
21
+ annotation = { 'annoTypeIdentifier': dict['type'] }
22
+
23
+ if ('client_id' in dict): annotation['clientId'] = str(dict['client_id'])
24
+ if ('offsets' in dict): annotation['offsets'] = dict['offsets']
25
+ if ('value' in dict): annotation['value'] = dict['value']
26
+ if ('bbox' in dict): annotation['bbox'] = dict['bbox']
27
+ if ('text_bounds' in dict): annotation['textBounds'] = dict['text_bounds']
28
+ if ('image_bounds' in dict): annotation['imageBounds'] = dict['image_bounds']
29
+ if ('layer' in dict): annotation['layerIdentifier'] = dict['layer']
30
+ if ('page' in dict): annotation['pageNumber'] = dict['page']
31
+ if ('endPage' in dict): annotation['endPageNumber'] = dict['endPage']
32
+ if ('reviewed' in dict): annotation['isReviewed'] = dict['reviewed']
33
+ if ('source' in dict): annotation['sourceIdentifier'] = dict['source']
34
+ if ('directory' in dict): annotation['directoryIdentifier'] = dict['directory']
35
+ if ('project' in dict): annotation['projectIdentifier'] = dict['project']
36
+
37
+ return annotation
@@ -0,0 +1,28 @@
1
+ from typing import Dict
2
+
3
+
4
+ class AnnotationRelation:
5
+
6
+ @staticmethod
7
+ def create_api_relation(dict: Dict):
8
+ """
9
+ Maps an sdk relation dict to an api annotation relation dict, for use in calls to the api.
10
+
11
+ AnnotationRelation parameters:
12
+ annotations: [Union[str, int], Union[str, int]]
13
+ type: str
14
+ value: str
15
+ reviewed bool (Optional)
16
+ project Union[str, int]
17
+ """
18
+ relation = {
19
+ 'predecessorId': str(dict['annotations'][0]),
20
+ 'successorId': str(dict['annotations'][1])
21
+ }
22
+
23
+ if ('type' in dict): relation['annoTypeIdentifier'] = dict['type']
24
+ if ('value' in dict): relation['value'] = dict['value']
25
+ if ('reviewed' in dict): relation['isReviewed'] = dict['reviewed']
26
+ if ('project' in dict): relation['projectIdentifier'] = dict['project']
27
+
28
+ return relation
annolab/api_helper.py ADDED
@@ -0,0 +1,97 @@
1
+ from typing import Dict, Any
2
+ import requests
3
+ from urllib import parse
4
+ import logging
5
+ from requests.models import Response
6
+
7
+ import annolab
8
+ from annolab import endpoints
9
+ from annolab.util.cached_property import cached_property
10
+
11
+ class ApiHelper(object):
12
+
13
+ def __init__(
14
+ self,
15
+ api_key = None,
16
+ api_url = 'https://api.annolab.ai',
17
+ ):
18
+ self.api_url = api_url
19
+ self.api_key = api_key or annolab.api_key
20
+
21
+
22
+ @property
23
+ def __auth_header(self, key: str = None):
24
+ key = key or self.api_key
25
+ return { 'Authorization': f'Api-Key {key}' }
26
+
27
+
28
+ @cached_property
29
+ def api_key_info(self):
30
+ return self.get_request(endpoints.ApiKey.get_api_key_info()).json()
31
+
32
+
33
+ '''
34
+ Returns the default group to use for the api key.
35
+ The default group is the group representing the single user.
36
+ '''
37
+ @cached_property
38
+ def default_owner(self):
39
+ default_owner = None
40
+ for group in self.api_key_info['groups']:
41
+ if (group['isSingleUser'] is True):
42
+ default_owner = group
43
+
44
+ return default_owner
45
+
46
+
47
+ def get_request(self, path: str, body: Dict[str, Any] = None, params: dict = None) -> Response:
48
+ resp = requests.get(
49
+ parse.urljoin(self.api_url, path),
50
+ headers=self.__auth_header,
51
+ json=body
52
+ )
53
+
54
+ self.__handle_non_2xx_response(resp)
55
+
56
+ return resp
57
+
58
+
59
+ def post_request(self, path: str, body: Dict[str, Any] = None, params: dict = None, timeout: float = 30.0):
60
+ resp = requests.post(
61
+ parse.urljoin(self.api_url, path),
62
+ headers=self.__auth_header,
63
+ json=body,
64
+ params=params,
65
+ timeout=timeout
66
+ )
67
+
68
+ self.__handle_non_2xx_response(resp)
69
+
70
+ return resp
71
+
72
+
73
+ def put_request(self, path: str, data: Any = None, headers = None, params: dict = None, timeout: float = 30.0):
74
+ resp = requests.put(
75
+ parse.urljoin(self.api_url, path),
76
+ headers=headers,
77
+ data=data,
78
+ params=params,
79
+ timeout=timeout
80
+ )
81
+
82
+ self.__handle_non_2xx_response(resp)
83
+
84
+ return resp
85
+
86
+
87
+ def __handle_non_2xx_response(self, resp: Response):
88
+ if (resp.status_code >= 300):
89
+ try:
90
+ resp_body = resp.json()
91
+ except:
92
+ resp_body = {}
93
+
94
+ message = resp_body['message'] if 'message' in resp_body else 'Unknown Error'
95
+
96
+ logging.error(f'{resp.request.method} {resp.request.path_url} failed with message: {message}')
97
+ resp.raise_for_status()
annolab/endpoints.py ADDED
@@ -0,0 +1,114 @@
1
+ class Project:
2
+
3
+ @staticmethod
4
+ def post_create():
5
+ return 'v1/project/create'
6
+
7
+
8
+ @staticmethod
9
+ def get_group_project(groupName: str, projectName: str):
10
+ return f'v1/project/{groupName}/{projectName}'
11
+
12
+
13
+ @staticmethod
14
+ def get_using_id(id: int):
15
+ return f'v1/project/{id}'
16
+
17
+
18
+ class Export:
19
+
20
+ @staticmethod
21
+ def post_export_project():
22
+ return 'v1/export/project'
23
+
24
+
25
+ class Directory:
26
+
27
+ @staticmethod
28
+ def post_create():
29
+ return 'v1/directory/create'
30
+
31
+
32
+ class Annotation:
33
+
34
+ @staticmethod
35
+ def post_create():
36
+ return 'v1/annotation/create'
37
+
38
+ @staticmethod
39
+ def post_bulk_create():
40
+ return 'v1/annotation/bulk-create'
41
+
42
+
43
+ class AnnotationType:
44
+
45
+ @staticmethod
46
+ def post_create():
47
+ return 'v1/annotation-type/create'
48
+
49
+
50
+ class AnnotationRelation:
51
+
52
+ @staticmethod
53
+ def post_create():
54
+ return 'v1/relation/create'
55
+
56
+
57
+ @staticmethod
58
+ def post_bulk_create():
59
+ return 'v1/relation/bulk-create'
60
+
61
+
62
+ class AnnotationLayer:
63
+
64
+ @staticmethod
65
+ def post_create():
66
+ return 'v1/layer/create'
67
+
68
+
69
+ class Source:
70
+
71
+ @staticmethod
72
+ def get_source_by_id(source_ref_id: int):
73
+ return f'v1/source/{source_ref_id}'
74
+
75
+
76
+ @staticmethod
77
+ def get_source_by_path(owner_name: str, project_name: str, directory_name: str, source_ref_name: str):
78
+ return f'v1/source/{owner_name}/{project_name}/{directory_name}/{source_ref_name}'
79
+
80
+
81
+ @staticmethod
82
+ def post_create_text():
83
+ return 'v1/source/create-text'
84
+
85
+
86
+ @staticmethod
87
+ def post_initialize_pdf():
88
+ return 'v1/source/init-pdf'
89
+
90
+
91
+ @staticmethod
92
+ def post_create_pdf():
93
+ return 'v1/source/create-pdf'
94
+
95
+
96
+ @staticmethod
97
+ def post_annotations(owner_name: str, project_name: str, directory_name: str, source_ref_name: str):
98
+ return f'v1/source/{owner_name}/{project_name}/{directory_name}/{source_ref_name}/annotations'
99
+
100
+
101
+ @staticmethod
102
+ def delete_using_name(owner_name: str, project_name: str, directory_name: str, source_ref_name: str):
103
+ return f'v1/source/{owner_name}/{project_name}/{directory_name}/{source_ref_name}'
104
+
105
+
106
+ @staticmethod
107
+ def delete_using_id(id: int):
108
+ return f'v1/source/{id}'
109
+
110
+
111
+ class ApiKey:
112
+ @staticmethod
113
+ def get_api_key_info():
114
+ return 'v1/api-key/info'