Skip to content

Commit 4c21217

Browse files
committed
Added approve/disapprove items
1 parent 32bc13d commit 4c21217

File tree

16 files changed

+288
-34
lines changed

16 files changed

+288
-34
lines changed

docs/source/superannotate.sdk.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ ______
8181
.. automethod:: superannotate.SAClient.unassign_items
8282
.. automethod:: superannotate.SAClient.get_item_metadata
8383
.. automethod:: superannotate.SAClient.set_annotation_statuses
84+
.. automethod:: superannotate.SAClient.set_approval_statuses
85+
.. automethod:: superannotate.SAClient.set_approval
8486

8587
----------
8688

requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ xmltodict==0.12.0
1010
opencv-python>=4.4.0.42
1111
wheel==0.35.1
1212
packaging>=20.4
13-
plotly==4.1.0
13+
plotly>=4.1.0
1414
ffmpeg-python>=0.2.0
1515
fire==0.4.0
1616
mixpanel==4.8.3

src/superannotate/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33
import typing
44

5-
__version__ = "4.4.9dev2"
5+
__version__ = "4.4.9dev4"
66

77
sys.path.append(os.path.split(os.path.realpath(__file__))[0])
88

src/superannotate/lib/app/interface/sdk_interface.py

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from lib.app.interface.types import AnnotationStatuses
2424
from lib.app.interface.types import AnnotationType
2525
from lib.app.interface.types import AnnotatorRole
26+
from lib.app.interface.types import ApprovalStatuses
2627
from lib.app.interface.types import AttachmentArg
2728
from lib.app.interface.types import AttachmentDict
2829
from lib.app.interface.types import ClassType
@@ -2332,7 +2333,7 @@ def search_items(
23322333
♦ “QualityCheck” \n
23332334
♦ “Returned” \n
23342335
♦ “Completed” \n
2335-
♦ “Skippe
2336+
♦ “Skip” \n
23362337
:type annotation_status: str
23372338
23382339
:param annotator_email: returns those items’ names that are assigned to the specified annotator.
@@ -2569,13 +2570,13 @@ def set_annotation_statuses(
25692570
:param project: project name or folder path (e.g., “project1/folder1”).
25702571
:type project: str
25712572
2572-
:param annotation_status: annotation status to set, should be one of.
2573-
“NotStarted”
2574-
“InProgress”
2575-
“QualityCheck”
2576-
“Returned”
2577-
“Completed”
2578-
“Skipped”
2573+
:param annotation_status: annotation status to set, should be one of. \n
2574+
“NotStarted” \n
2575+
“InProgress” \n
2576+
“QualityCheck” \n
2577+
“Returned” \n
2578+
“Completed” \n
2579+
“Skipped” \n
25792580
:type annotation_status: str
25802581
25812582
:param items: item names to set the mentioned status for. If None, all the items in the project will be used.
@@ -3021,3 +3022,32 @@ def add_items_to_subset(
30213022

30223023
return response.data
30233024

3025+
def set_approval_statuses(
3026+
self,
3027+
project: NotEmptyStr,
3028+
approval_status: Optional[ApprovalStatuses],
3029+
items: Optional[List[NotEmptyStr]] = None,
3030+
):
3031+
"""Sets annotation statuses of items
3032+
3033+
:param project: project name or folder path (e.g., “project1/folder1”).
3034+
:type project: str
3035+
3036+
:param approval_status: approval status to set, should be one of. \n
3037+
♦ None \n
3038+
♦ “Approved” \n
3039+
♦ “Disapproved” \n
3040+
:type approval_status: str
3041+
3042+
:param items: item names to set the mentioned status for. If None, all the items in the project will be used.
3043+
:type items: list of strs
3044+
"""
3045+
project, folder = self.controller.get_project_folder_by_path(project)
3046+
response = self.controller.items.set_approval_statuses(
3047+
project=project,
3048+
folder=folder,
3049+
approval_status=approval_status,
3050+
item_names=items,
3051+
)
3052+
if response.errors:
3053+
raise AppException(response.errors)

src/superannotate/lib/app/interface/types.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import Union
66

77
from lib.core.enums import AnnotationStatus
8+
from lib.core.enums import ApprovalStatus
89
from lib.core.enums import BaseTitledEnum
910
from lib.core.enums import ClassTypeEnum
1011
from lib.core.enums import FolderStatus
@@ -199,6 +200,16 @@ def validate(cls, value: Union[str]) -> Union[str]:
199200
return value
200201

201202

203+
class ApprovalStatuses(StrictStr):
204+
@classmethod
205+
def validate(cls, value: Union[str]) -> Union[str]:
206+
if value.lower() not in ApprovalStatus.values():
207+
raise TypeError(
208+
f"Available an approval_statuses are {', '.join(ApprovalStatus.titles())}. "
209+
)
210+
return value
211+
212+
202213
def validate_arguments(func):
203214
@wraps(func)
204215
def wrapped(self, *args, **kwargs):

src/superannotate/lib/app/server/README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ Usage
1010

1111
.. code-block:: bash
1212
13-
./run.sh --token=<your team token>
13+
./run.sh --token=<your team token>

src/superannotate/lib/core/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from superannotate.lib.core.config import Config
44
from superannotate.lib.core.enums import AnnotationStatus
5+
from superannotate.lib.core.enums import ApprovalStatus
56
from superannotate.lib.core.enums import FolderStatus
67
from superannotate.lib.core.enums import ImageQuality
78
from superannotate.lib.core.enums import ProjectStatus
@@ -16,7 +17,7 @@
1617
CONFIG_PATH = "~/.superannotate/config.json"
1718
CONFIG_FILE_LOCATION = expanduser(CONFIG_PATH)
1819
LOG_FILE_LOCATION = expanduser("~/.superannotate")
19-
BACKEND_URL = "https://api.annotate.online"
20+
BACKEND_URL = "https://api.superannotate.com"
2021

2122
DEFAULT_IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "tif", "tiff", "webp", "bmp"]
2223
DEFAULT_FILE_EXCLUDE_PATTERNS = ["___save.png", "___fuse.png"]
@@ -121,6 +122,7 @@
121122
SegmentationStatus,
122123
ImageQuality,
123124
AnnotationStatus,
125+
ApprovalStatus,
124126
CONFIG_FILE_LOCATION,
125127
CONFIG_PATH,
126128
BACKEND_URL,

src/superannotate/lib/core/enums.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __get__(self, instance, owner):
1212

1313
class BaseTitledEnum(int, Enum):
1414
def __new__(cls, title, value):
15-
obj = int.__new__(cls, value)
15+
obj = super().__new__(cls, value)
1616
obj._value_ = value
1717
obj.__doc__ = title
1818
obj._type = "titled_enum"
@@ -40,15 +40,15 @@ def get_name(cls, value):
4040
@classmethod
4141
def get_value(cls, name):
4242
for enum in list(cls):
43-
if enum.__doc__.lower() == name.lower():
43+
if enum.__doc__ and name and enum.__doc__.lower() == name.lower():
4444
if isinstance(enum.value, int):
4545
if enum.value < 0:
4646
return ""
4747
return enum.value
4848

4949
@classmethod
5050
def values(cls):
51-
return [enum.__doc__.lower() for enum in list(cls)]
51+
return [enum.__doc__.lower() if enum else None for enum in list(cls)]
5252

5353
@classmethod
5454
def titles(cls):
@@ -64,6 +64,12 @@ def __hash__(self):
6464
return hash(self.name)
6565

6666

67+
class ApprovalStatus(BaseTitledEnum):
68+
NONE = None, 0
69+
DISAPPROVED = "Disapproved", 1
70+
APPROVED = "Approved", 2
71+
72+
6773
class AnnotationTypes(str, Enum):
6874
BBOX = "bbox"
6975
EVENT = "event"
@@ -170,9 +176,3 @@ class SegmentationStatus(BaseTitledEnum):
170176
IN_PROGRESS = "InProgress", 2
171177
COMPLETED = "Completed", 3
172178
FAILED = "Failed", 4
173-
174-
175-
class ApprovalStatus(BaseTitledEnum):
176-
NONE = None, 0
177-
DISAPPROVED = "disapproved", 1
178-
APPROVED = "approved", 2

src/superannotate/lib/core/serviceproviders.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,16 @@ def set_statuses(
293293
) -> ServiceResponse:
294294
raise NotImplementedError
295295

296+
@abstractmethod
297+
def set_approval_statuses(
298+
self,
299+
project: entities.ProjectEntity,
300+
folder: entities.FolderEntity,
301+
item_names: List[str],
302+
approval_status: int,
303+
) -> ServiceResponse:
304+
raise NotImplementedError
305+
296306
@abstractmethod
297307
def delete_multiple(
298308
self, project: entities.ProjectEntity, item_ids: List[int]

src/superannotate/lib/core/usecases/annotations.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
BIG_FILE_THRESHOLD = 15 * 1024 * 1024
5454
ANNOTATION_CHUNK_SIZE_MB = 10 * 1024 * 1024
5555
URI_THRESHOLD = 4 * 1024 - 120
56-
nest_asyncio.apply()
5756

5857

5958
@dataclass
@@ -376,6 +375,7 @@ def execute(self):
376375
len(items_to_upload), description="Uploading Annotations"
377376
)
378377
try:
378+
nest_asyncio.apply()
379379
asyncio.run(self.run_workers(items_to_upload))
380380
except Exception:
381381
logger.debug(traceback.format_exc())
@@ -720,6 +720,7 @@ def execute(self):
720720
except KeyError:
721721
missing_annotations.append(name)
722722
try:
723+
nest_asyncio.apply()
723724
asyncio.run(self.run_workers(items_to_upload))
724725
except Exception:
725726
logger.debug(traceback.format_exc())
@@ -917,6 +918,7 @@ def execute(self):
917918
json.dump(annotation_json, annotation_file)
918919
size = annotation_file.tell()
919920
annotation_file.seek(0)
921+
nest_asyncio.apply()
920922
if size > BIG_FILE_THRESHOLD:
921923
uploaded = asyncio.run(
922924
self._service_provider.annotations.upload_big_annotation(
@@ -1109,6 +1111,7 @@ def execute(self):
11091111
)
11101112
small_annotations = [x["name"] for x in items["small"]]
11111113
try:
1114+
nest_asyncio.apply()
11121115
annotations = asyncio.run(
11131116
self.run_workers(items["large"], small_annotations)
11141117
)
@@ -1355,14 +1358,6 @@ def download_annotation_classes(self, path: str):
13551358
def get_items_count(path: str):
13561359
return sum([len(files) for r, d, files in os.walk(path)])
13571360

1358-
@staticmethod
1359-
def coroutine_wrapper(coroutine):
1360-
loop = asyncio.new_event_loop()
1361-
asyncio.set_event_loop(loop)
1362-
count = loop.run_until_complete(coroutine)
1363-
loop.close()
1364-
return count
1365-
13661361
async def download_big_annotations(self, queue_idx, export_path):
13671362
while True:
13681363
cur_queue = self._big_file_queues[queue_idx]
@@ -1463,6 +1458,7 @@ def execute(self):
14631458
if not folders:
14641459
folders.append(self._folder)
14651460
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
1461+
nest_asyncio.apply()
14661462
futures = []
14671463
for folder in folders:
14681464
if not self._item_names:

0 commit comments

Comments
 (0)