11import copy
22from typing import List
33
4- import superannotate .lib .core as constances
4+ import superannotate .lib .core as constants
55from lib .core .conditions import Condition
66from lib .core .conditions import CONDITION_EQ as EQ
77from lib .core .entities import AttachmentEntity
2020from lib .core .serviceproviders import SuperannotateServiceProvider
2121from lib .core .usecases .base import BaseReportableUseCase
2222from lib .core .usecases .base import BaseUseCase
23+ from lib .core .entities import ImageEntity
2324from superannotate .logger import get_default_logger
2425
25-
2626logger = get_default_logger ()
2727
28+ class GetBulkItems (BaseUseCase ):
29+ def __init__ (
30+ self ,
31+ service : SuperannotateServiceProvider ,
32+ project_id : int ,
33+ team_id : int ,
34+ folder_id : int ,
35+ items : List [str ],
36+ ):
37+ super ().__init__ ()
38+ self ._service = service
39+ self ._project_id = project_id
40+ self ._team_id = team_id
41+ self ._folder_id = folder_id
42+ self ._items = items
43+ self ._chunk_size = 500
44+
45+ def execute (self ):
46+ res = []
47+ for i in range (0 , len (self ._items ), self ._chunk_size ):
48+ response = self ._service .get_bulk_items (
49+ project_id = self ._project_id ,
50+ team_id = self ._team_id ,
51+ folder_id = self ._folder_id ,
52+ items = self ._items [i : i + self ._chunk_size ], # noqa: E203
53+ )
54+
55+ if not response .ok :
56+ raise AppException (response .error )
57+ #TODO stop using Image Entity when it gets deprecated and from_dict gets implemented for items
58+ res += [ImageEntity .from_dict (** item ) for item in response .data ]
59+ self ._response .data = res
60+ return self ._response
2861
2962class GetItem (BaseReportableUseCase ):
3063 def __init__ (
@@ -43,22 +76,22 @@ def __init__(
4376
4477 @staticmethod
4578 def serialize_entity (entity : Entity , project : ProjectEntity ):
46- if project .upload_state != constances .UploadState .EXTERNAL .value :
79+ if project .upload_state != constants .UploadState .EXTERNAL .value :
4780 entity .url = None
4881 if project .type in (
49- constances .ProjectType .VECTOR .value ,
50- constances .ProjectType .PIXEL .value ,
82+ constants .ProjectType .VECTOR .value ,
83+ constants .ProjectType .PIXEL .value ,
5184 ):
5285 tmp_entity = entity
53- if project .type == constances .ProjectType .VECTOR .value :
86+ if project .type == constants .ProjectType .VECTOR .value :
5487 entity .segmentation_status = None
55- if project .upload_state == constances .UploadState .EXTERNAL .value :
88+ if project .upload_state == constants .UploadState .EXTERNAL .value :
5689 tmp_entity .prediction_status = None
5790 tmp_entity .segmentation_status = None
5891 return TmpImageEntity (** tmp_entity .dict (by_alias = True ))
59- elif project .type == constances .ProjectType .VIDEO .value :
92+ elif project .type == constants .ProjectType .VIDEO .value :
6093 return VideoEntity (** entity .dict (by_alias = True ))
61- elif project .type == constances .ProjectType .DOCUMENT .value :
94+ elif project .type == constants .ProjectType .DOCUMENT .value :
6295 return DocumentEntity (** entity .dict (by_alias = True ))
6396 return entity
6497
@@ -104,7 +137,7 @@ def validate_query(self):
104137 self ._query = response ["parsedQuery" ]
105138 else :
106139 raise AppException ("Incorrect query." )
107- if self ._project .sync_status != constances .ProjectState .SYNCED .value :
140+ if self ._project .sync_status != constants .ProjectState .SYNCED .value :
108141 raise AppException ("Data is not synced." )
109142
110143 def execute (self ) -> Response :
@@ -210,33 +243,31 @@ def __init__(
210243 self ._user = user
211244 self ._service = service
212245
213- def validate_user (
246+ def validate_item_names (
214247 self ,
215248 ):
216-
217- for c in self ._project .users :
218- if c ["user_id" ] == self ._user :
219- return True
220-
221- raise AppValidationException (
222- f"{ self ._user } is not a verified contributor for the { self ._project .name } "
223- )
249+ self ._item_names = list (set (self ._item_names ))
224250
225251 def execute (self ):
252+ cnt_assigned = 0
253+ total_count = len (self ._item_names )
226254 if self .is_valid ():
227255 for i in range (0 , len (self ._item_names ), self .CHUNK_SIZE ):
228- is_assigned = self ._service .assign_items (
256+ response = self ._service .assign_items (
229257 team_id = self ._project .team_id ,
230258 project_id = self ._project .id ,
231259 folder_name = self ._folder .name ,
232260 user = self ._user ,
233261 item_names = self ._item_names [i : i + self .CHUNK_SIZE ], # noqa: E203
234262 )
235- if not is_assigned :
236- self ._response .errors = AppException (
237- f"Cant assign { ', ' .join (self ._item_names [i : i + self .CHUNK_SIZE ])} "
238- )
239- continue
263+ if not response .ok and response .error : # User not found
264+ self ._response .errors += response .error
265+ return self ._response
266+
267+ cnt_assigned += response .data ["successCount" ]
268+ logger .info (
269+ f"Assigned { cnt_assigned } /{ total_count } items to user { self ._user } "
270+ )
240271 return self ._response
241272
242273
@@ -284,13 +315,13 @@ def __init__(
284315 attachments : List [AttachmentEntity ],
285316 annotation_status : str ,
286317 backend_service_provider : SuperannotateServiceProvider ,
287- upload_state_code : int = constances .UploadState .EXTERNAL .value ,
318+ upload_state_code : int = constants .UploadState .EXTERNAL .value ,
288319 ):
289320 super ().__init__ (reporter )
290321 self ._project = project
291322 self ._folder = folder
292323 self ._attachments = attachments
293- self ._annotation_status_code = constances .AnnotationStatus .get_value (
324+ self ._annotation_status_code = constants .AnnotationStatus .get_value (
294325 annotation_status
295326 )
296327 self ._upload_state_code = upload_state_code
@@ -313,18 +344,18 @@ def validate_limitations(self):
313344 if not response .ok :
314345 raise AppValidationException (response .error )
315346 if attachments_count > response .data .folder_limit .remaining_image_count :
316- raise AppValidationException (constances .ATTACH_FOLDER_LIMIT_ERROR_MESSAGE )
347+ raise AppValidationException (constants .ATTACH_FOLDER_LIMIT_ERROR_MESSAGE )
317348 elif attachments_count > response .data .project_limit .remaining_image_count :
318- raise AppValidationException (constances .ATTACH_PROJECT_LIMIT_ERROR_MESSAGE )
349+ raise AppValidationException (constants .ATTACH_PROJECT_LIMIT_ERROR_MESSAGE )
319350 elif (
320351 response .data .user_limit
321352 and attachments_count > response .data .user_limit .remaining_image_count
322353 ):
323- raise AppValidationException (constances .ATTACH_USER_LIMIT_ERROR_MESSAGE )
354+ raise AppValidationException (constants .ATTACH_USER_LIMIT_ERROR_MESSAGE )
324355
325356 def validate_upload_state (self ):
326- if self ._project .upload_state == constances .UploadState .BASIC .value :
327- raise AppValidationException (constances .ATTACHING_UPLOAD_STATE_ERROR )
357+ if self ._project .upload_state == constants .UploadState .BASIC .value :
358+ raise AppValidationException (constants .ATTACHING_UPLOAD_STATE_ERROR )
328359
329360 @staticmethod
330361 def generate_meta ():
@@ -411,9 +442,9 @@ def _validate_limitations(self, items_count):
411442 if not response .ok :
412443 raise AppValidationException (response .error )
413444 if items_count > response .data .folder_limit .remaining_image_count :
414- raise AppValidationException (constances .COPY_FOLDER_LIMIT_ERROR_MESSAGE )
445+ raise AppValidationException (constants .COPY_FOLDER_LIMIT_ERROR_MESSAGE )
415446 if items_count > response .data .project_limit .remaining_image_count :
416- raise AppValidationException (constances .COPY_PROJECT_LIMIT_ERROR_MESSAGE )
447+ raise AppValidationException (constants .COPY_PROJECT_LIMIT_ERROR_MESSAGE )
417448
418449 def validate_item_names (self ):
419450 if self ._item_names :
@@ -539,9 +570,9 @@ def _validate_limitations(self, items_count):
539570 if not response .ok :
540571 raise AppValidationException (response .error )
541572 if items_count > response .data .folder_limit .remaining_image_count :
542- raise AppValidationException (constances .MOVE_FOLDER_LIMIT_ERROR_MESSAGE )
573+ raise AppValidationException (constants .MOVE_FOLDER_LIMIT_ERROR_MESSAGE )
543574 if items_count > response .data .project_limit .remaining_image_count :
544- raise AppValidationException (constances .MOVE_PROJECT_LIMIT_ERROR_MESSAGE )
575+ raise AppValidationException (constants .MOVE_PROJECT_LIMIT_ERROR_MESSAGE )
545576
546577 def execute (self ):
547578 if self .is_valid ():
@@ -599,7 +630,7 @@ def __init__(
599630 self ._folder = folder
600631 self ._item_names = item_names
601632 self ._items = items
602- self ._annotation_status_code = constances .AnnotationStatus .get_value (
633+ self ._annotation_status_code = constants .AnnotationStatus .get_value (
603634 annotation_status
604635 )
605636 self ._backend_service = backend_service_provider
@@ -651,3 +682,57 @@ def execute(self):
651682 self ._response .errors = AppException (self .ERROR_MESSAGE )
652683 break
653684 return self ._response
685+
686+ class DeleteItemsUseCase (BaseUseCase ):
687+ CHUNK_SIZE = 1000
688+
689+ def __init__ (
690+ self ,
691+ project : ProjectEntity ,
692+ folder : FolderEntity ,
693+ backend_service_provider : SuperannotateServiceProvider ,
694+ items : BaseReadOnlyRepository ,
695+ item_names : List [str ] = None ,
696+ ):
697+ super ().__init__ ()
698+ self ._project = project
699+ self ._folder = folder
700+ self ._items = items
701+ self ._backend_service = backend_service_provider
702+ self ._item_names = item_names
703+
704+ def execute (self ):
705+ if self .is_valid ():
706+ if self ._item_names :
707+ item_ids = [
708+ item .uuid
709+ for item in GetBulkItems (
710+ service = self ._backend_service ,
711+ project_id = self ._project .id ,
712+ team_id = self ._project .team_id ,
713+ folder_id = self ._folder .uuid ,
714+ items = self ._item_names ,
715+ )
716+ .execute ()
717+ .data
718+ ]
719+ else :
720+ condition = (
721+ Condition ("team_id" , self ._project .team_id , EQ )
722+ & Condition ("project_id" , self ._project .id , EQ )
723+ & Condition ("folder_id" , self ._folder .uuid , EQ )
724+ )
725+ item_ids = [item .id for item in self ._items .get_all (condition )]
726+
727+ for i in range (0 , len (item_ids ), self .CHUNK_SIZE ):
728+ response = self ._backend_service .delete_items (
729+ project_id = self ._project .id ,
730+ team_id = self ._project .team_id ,
731+ item_ids = item_ids [i : i + self .CHUNK_SIZE ], # noqa: E203
732+ )
733+
734+ logger .info (
735+ f"Items deleted in project { self ._project .name } { '/' + self ._folder .name if not self ._folder .is_root else '' } "
736+ )
737+
738+ return self ._response
0 commit comments