Skip to content

Commit 8a53e72

Browse files
Narek MkhitaryanNarek Mkhitaryan
authored andcommitted
updates in create_project
1 parent f4080c2 commit 8a53e72

File tree

3 files changed

+76
-16
lines changed

3 files changed

+76
-16
lines changed

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

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,37 +1133,38 @@ def create_project(
11331133
workflow: str = None,
11341134
form: dict = None,
11351135
):
1136-
"""Create a new project in the team.
1136+
"""Creates a new project in the team. For Multimodal projects, you must provide a valid form object,
1137+
which serves as a template determining the layout and behavior of the project's interface.
11371138
1138-
:param project_name: the new project's name
1139+
:param project_name: The new project's name.
11391140
:type project_name: str
11401141
1141-
:param project_description: the new project's description
1142+
:param project_description: The new project's description.
11421143
:type project_description: str
11431144
1144-
:param project_type: the new project type, Vector, Pixel, Video, Document, Tiled, PointCloud, Multimodal.
1145+
:param project_type: The project type. Supported types: 'Vector', 'Pixel', 'Video', 'Document', 'Tiled', 'PointCloud', 'Multimodal'.
11451146
:type project_type: str
11461147
11471148
:param settings: list of settings objects
11481149
:type settings: list of dicts
11491150
1150-
:param classes: list of class objects
1151+
:param classes: List of class objects. Not allowed for 'Multimodal' projects.
11511152
:type classes: list of dicts
11521153
1153-
:param workflows: Deprecated
1154+
:param workflows: Deprecated. Do not use.
11541155
:type workflows: list of dicts
11551156
1156-
:param workflow: the name of the workflow already created within the team, which must match exactly.
1157-
If None, the default “System workflow” workflow will be set.
1157+
:param workflow: Name of the workflow already created within the team (must match exactly). If None, the default "System workflow" will be used.
11581158
:type workflow: str
11591159
1160-
:param instructions_link: str of instructions URL
1160+
:param instructions_link: URL for project instructions.
11611161
:type instructions_link: str
11621162
1163-
:param form: form object that will be used for the MULTIMODAL project.
1163+
:param form: Required for Multimodal projects. Must be a JSON object that conforms to SuperAnnotate’s schema
1164+
for Multimodal form templates, as used in the Multimodal Form Editor.
11641165
:type form: dict
11651166
1166-
:return: dict object metadata the new project
1167+
:return: Metadata of the newly created project.
11671168
:rtype: dict
11681169
"""
11691170
if workflows is not None:
@@ -1177,9 +1178,14 @@ def create_project(
11771178
else:
11781179
settings = []
11791180
if ProjectType(project_type) == ProjectType.MULTIMODAL:
1180-
logger.error("Form is required for Multimodal projects.")
1181+
if not form:
1182+
raise AppException(
1183+
"A form object is required when creating a Multimodal project."
1184+
)
11811185
if classes is not None:
1182-
raise AppException("You can't provide classes for Multimodal projects.")
1186+
raise AppException(
1187+
"Classes cannot be provided for Multimodal projects."
1188+
)
11831189
settings.append(SettingEntity(attribute="TemplateState", value=1))
11841190
if classes:
11851191
classes = parse_obj_as(List[AnnotationClassEntity], classes)

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,11 +1008,13 @@ def __init__(
10081008

10091009
def validate_form(self):
10101010
if self._form is None:
1011-
raise AppException("Form is not provided.")
1011+
raise AppException("Form was not provided.")
10121012
try:
10131013
self._entity = FormModel(**self._form)
10141014
except ValidationError:
1015-
raise AppException("Form is not valid.")
1015+
raise AppException(
1016+
"The provided form object is invalid or does not match the required schema."
1017+
)
10161018

10171019
def execute(self) -> Response:
10181020
if self.is_valid():
@@ -1028,6 +1030,8 @@ def execute(self) -> Response:
10281030
self._response.data = response.data
10291031
else:
10301032
self._response.errors = response.error
1031-
logger.error("Failed attach form deleting the project.")
1033+
logger.error(
1034+
"Failed to attach form to the project. Project will be deleted."
1035+
)
10321036
self._service_provider.projects.delete(self._project)
10331037
return self._response

tests/integration/projects/test_multimodal_creation.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,53 @@ def test_create_project_with_form_classes(self):
131131
assert (
132132
gen_attr["default"] == exp_attr["default"]
133133
), f"Class {i}, group {j}, attr {k}: default mismatch"
134+
135+
def test_create_project_multimodal_no_form(self):
136+
"""Test that multimodal project creation fails when no form is provided"""
137+
with self.assertRaises(AppException) as context:
138+
sa.create_project(
139+
self.PROJECT_NAME,
140+
"desc",
141+
self.PROJECT_TYPE,
142+
)
143+
assert "A form object is required when creating a Multimodal project." in str(
144+
context.exception
145+
)
146+
147+
def test_create_project_multimodal_with_classes(self):
148+
"""Test that multimodal project creation fails when classes are provided"""
149+
classes = [
150+
{
151+
"type": 1,
152+
"name": "Test Class",
153+
"color": "#FF0000",
154+
}
155+
]
156+
157+
with self.assertRaises(AppException) as context:
158+
sa.create_project(
159+
self.PROJECT_NAME,
160+
"desc",
161+
self.PROJECT_TYPE,
162+
classes=classes,
163+
form={"components": []},
164+
)
165+
assert "Classes cannot be provided for Multimodal projects." in str(
166+
context.exception
167+
)
168+
169+
def test_create_project_invalid_form_structure(self):
170+
"""Test project creation with structurally invalid form data"""
171+
invalid_form = {
172+
"invalid_key": [
173+
{"type": "invalid_component", "missing_required_fields": True}
174+
]
175+
}
176+
177+
with self.assertRaises(AppException):
178+
sa.create_project(
179+
self.PROJECT_NAME,
180+
"desc",
181+
self.PROJECT_TYPE,
182+
form=invalid_form,
183+
)

0 commit comments

Comments
 (0)