55import os
66import sys
77import tempfile
8+ import warnings
89from pathlib import Path
910from typing import Callable
1011from typing import Dict
5152from lib .core .conditions import Condition
5253from lib .core .conditions import EmptyCondition
5354from lib .core .entities import AttachmentEntity
55+ from lib .core .entities import WorkflowEntity
5456from lib .core .entities import SettingEntity
5557from lib .core .entities .classes import AnnotationClassEntity
5658from lib .core .entities .classes import AttributeGroup
@@ -233,7 +235,7 @@ def search_projects(
233235 self ,
234236 name : Optional [NotEmptyStr ] = None ,
235237 return_metadata : bool = False ,
236- include_complete_image_count : bool = False ,
238+ include_complete_item_count : bool = False ,
237239 status : Optional [Union [PROJECT_STATUS , List [PROJECT_STATUS ]]] = None ,
238240 ):
239241 """
@@ -246,8 +248,9 @@ def search_projects(
246248 :param return_metadata: return metadata of projects instead of names
247249 :type return_metadata: bool
248250
249- :param include_complete_image_count: return projects that have completed images and include the number of completed images in response.
250- :type include_complete_image_count: bool
251+ :param include_complete_item_count: return projects that have completed items and include
252+ the number of completed items in response.
253+ :type include_complete_item_count: bool
251254
252255 :param status: search projects via project status
253256 :type status: str
@@ -265,9 +268,9 @@ def search_projects(
265268 condition = Condition .get_empty_condition ()
266269 if name :
267270 condition &= Condition ("name" , name , EQ )
268- if include_complete_image_count :
271+ if include_complete_item_count :
269272 condition &= Condition (
270- "completeImagesCount" , include_complete_image_count , EQ
273+ "completeImagesCount" , include_complete_item_count , EQ
271274 )
272275 for status in statuses :
273276 condition &= Condition (
@@ -280,7 +283,13 @@ def search_projects(
280283 if return_metadata :
281284 return [
282285 ProjectSerializer (project ).serialize (
283- exclude = {"settings" , "workflows" , "contributors" , "classes" }
286+ exclude = {
287+ "settings" ,
288+ "workflows" ,
289+ "contributors" ,
290+ "classes" ,
291+ "item_count" ,
292+ }
284293 )
285294 for project in response .data
286295 ]
@@ -293,6 +302,9 @@ def create_project(
293302 project_description : NotEmptyStr ,
294303 project_type : PROJECT_TYPE ,
295304 settings : List [Setting ] = None ,
305+ classes : List [AnnotationClassEntity ] = None ,
306+ workflows : List = None ,
307+ instructions_link : str = None ,
296308 ):
297309 """Create a new project in the team.
298310
@@ -308,25 +320,71 @@ def create_project(
308320 :param settings: list of settings objects
309321 :type settings: list of dicts
310322
323+ :param classes: list of class objects
324+ :type classes: list of dicts
325+
326+ :param workflows: list of information for each step
327+ :type workflows: list of dicts
328+
329+ :param instructions_link: str of instructions URL
330+ :type instructions_link: str
331+
311332 :return: dict object metadata the new project
312333 :rtype: dict
313334 """
335+ if workflows :
336+ if project_type .capitalize () not in (
337+ constants .ProjectType .VECTOR .name ,
338+ constants .ProjectType .PIXEL .name ,
339+ ):
340+ raise AppException (
341+ f"Workflow is not supported in { project_type } project."
342+ )
343+ parse_obj_as (List [WorkflowEntity ], workflows )
344+ if workflows and not classes :
345+ raise AppException (
346+ "Project with workflows can not be created without classes."
347+ )
314348 if settings :
315349 settings = parse_obj_as (List [SettingEntity ], settings )
316350 else :
317351 settings = []
318- response = self .controller .projects .create (
352+ if classes :
353+ classes = parse_obj_as (List [AnnotationClassEntity ], classes )
354+ if workflows and classes :
355+ invalid_classes = []
356+ class_names = [_class .name for _class in classes ]
357+ for step in workflows :
358+ if step ["className" ] not in class_names :
359+ invalid_classes .append (step ["className" ])
360+ if invalid_classes :
361+ raise AppException (
362+ f"There are no [{ ', ' .join (invalid_classes )} ] classes created in the project."
363+ )
364+ project_response = self .controller .projects .create (
319365 entities .ProjectEntity (
320366 name = project_name ,
321367 description = project_description ,
322368 type = constants .ProjectType .get_value (project_type ),
323369 settings = settings ,
370+ instructions_link = instructions_link ,
324371 )
325372 )
326- if response .errors :
327- raise AppException (response .errors )
328-
329- return ProjectSerializer (response .data ).serialize ()
373+ project_response .raise_for_status ()
374+ project = project_response .data
375+ if classes :
376+ classes_response = self .controller .annotation_classes .create_multiple (
377+ project , classes
378+ )
379+ classes_response .raise_for_status ()
380+ project .classes = classes_response .data
381+ if workflows :
382+ workflow_response = self .controller .projects .set_workflows (
383+ project , workflows
384+ )
385+ workflow_response .raise_for_status ()
386+ project .workflows = self .controller .projects .list_workflow (project ).data
387+ return ProjectSerializer (project ).serialize ()
330388
331389 def create_project_from_metadata (self , project_metadata : Project ):
332390 """Create a new project in the team using project metadata object dict.
@@ -340,6 +398,10 @@ def create_project_from_metadata(self, project_metadata: Project):
340398 :return: dict object metadata the new project
341399 :rtype: dict
342400 """
401+ deprecation_msg = '"create_project_from_metadata" is deprecated and replaced by "create_project"'
402+ warnings .warn (deprecation_msg , DeprecationWarning )
403+ logger .warning (deprecation_msg )
404+
343405 project_metadata = project_metadata .dict ()
344406 if project_metadata ["type" ] not in enums .ProjectType .titles ():
345407 raise AppException (
@@ -557,7 +619,7 @@ def get_project_metadata(
557619 include_settings : Optional [StrictBool ] = False ,
558620 include_workflow : Optional [StrictBool ] = False ,
559621 include_contributors : Optional [StrictBool ] = False ,
560- include_complete_image_count : Optional [StrictBool ] = False ,
622+ include_complete_item_count : Optional [StrictBool ] = False ,
561623 ):
562624 """Returns project metadata
563625
@@ -576,9 +638,9 @@ def get_project_metadata(
576638 the key "contributors"
577639 :type include_contributors: bool
578640
579- :param include_complete_image_count : enables project complete image count output under
580- the key "completed_images_count "
581- :type include_complete_image_count : bool
641+ :param include_complete_item_count : enables project complete item count output under
642+ the key "completed_items_count "
643+ :type include_complete_item_count : bool
582644
583645 :return: metadata of project
584646 :rtype: dict
@@ -591,7 +653,7 @@ def get_project_metadata(
591653 include_settings ,
592654 include_workflow ,
593655 include_contributors ,
594- include_complete_image_count ,
656+ include_complete_item_count ,
595657 )
596658 if response .errors :
597659 raise AppException (response .errors )
@@ -968,7 +1030,13 @@ def get_project_image_count(
9681030 :return: number of images in the project
9691031 :rtype: int
9701032 """
1033+ deprecation_msg = (
1034+ "“get_project_image_count” is deprecated and replaced"
1035+ " by “item_count” value will be included in project metadata."
1036+ )
9711037
1038+ warnings .warn (deprecation_msg , DeprecationWarning )
1039+ logger .warning (deprecation_msg )
9721040 project_name , folder_name = extract_project_folder (project )
9731041
9741042 response = self .controller .get_project_image_count (
0 commit comments