Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9a17031
Keypoint support draft
VaghinakDev Apr 28, 2025
2615291
Update tests delaysd
VaghinakDev May 14, 2025
7d883ad
Add keypoint handling
VaghinakDev May 16, 2025
fdc5f44
Update llm upload
VaghinakDev May 16, 2025
71592f3
Update version
VaghinakDev May 16, 2025
06c7c1e
Merge branch 'develop' into FRIDAY_3726
VaghinakDev May 16, 2025
4b25866
Merge pull request #787 from superannotateai/FRIDAY_3726
VaghinakDev May 16, 2025
f03b2ba
Fix connections validaiton
VaghinakDev May 16, 2025
35fbaab
Merge pull request #788 from superannotateai/FRIDAY_3726
VaghinakDev May 16, 2025
1b89781
fix in set_project_steps
May 19, 2025
30f9bb6
Merge pull request #789 from superannotateai/fix_set_steps
nareksa May 19, 2025
9e735b1
fix get_steps
May 19, 2025
1ddb15b
Merge pull request #790 from superannotateai/fix_set_steps
nareksa May 19, 2025
be2c659
fix get_steps
May 19, 2025
890c86a
Merge pull request #791 from superannotateai/fix_set_steps
nareksa May 19, 2025
fe04a90
fix upload_annotations
May 19, 2025
8f158d7
Merge pull request #792 from superannotateai/fix_upload_annotations
nareksa May 19, 2025
0354482
fix _attach_categories in upload multimodal annotations
May 20, 2025
d831c09
Merge pull request #793 from superannotateai/fix_upload_annotations
nareksa May 20, 2025
37b09fa
version update
May 20, 2025
47899b4
Merge pull request #794 from superannotateai/fix_upload_annotations
nareksa May 20, 2025
0772d95
fix in docs
May 20, 2025
4d040ec
Add user permissions
VaghinakDev May 27, 2025
1f138fe
Merge pull request #796 from superannotateai/FRIDAY-3794
VaghinakDev May 27, 2025
c9ac6ef
Merge pull request #795 from superannotateai/fix_docs
VaghinakDev May 27, 2025
5afb4cc
Update __init__.py
VaghinakDev Jun 5, 2025
40c579d
Update CHANGELOG.rst
VaghinakDev Jun 5, 2025
40ba873
Update CHANGELOG.rst
VaghinakDev Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ History

All release highlights of this project will be documented in this file.

4.4.36 - June 05, 2025
______________________

**Updated**

- ``SAClient.get_project_steps`` and ``SAClient.get_project_steps`` now support keypoint workflows, enabling structured step configuration with class IDs, attributes, and step connections.
- ``SAClient.list_users`` now returns user-specific permission states for paused, allow_orchestrate, allow_run_explore, and allow_view_sdk_token.


4.4.35 - May 2, 2025
____________________
Expand Down
2 changes: 1 addition & 1 deletion docs/source/api_reference/api_project.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ Projects
.. automethod:: superannotate.SAClient.add_contributors_to_project
.. automethod:: superannotate.SAClient.get_project_settings
.. automethod:: superannotate.SAClient.set_project_default_image_quality_in_editor
.. automethod:: superannotate.SAClient.set_project_steps
.. automethod:: superannotate.SAClient.get_project_steps
.. automethod:: superannotate.SAClient.set_project_steps
.. automethod:: superannotate.SAClient.get_component_config
2 changes: 1 addition & 1 deletion src/superannotate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import sys


__version__ = "4.4.35"
__version__ = "4.4.36"


os.environ.update({"sa_version": __version__})
Expand Down
81 changes: 74 additions & 7 deletions src/superannotate/lib/app/interface/sdk_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@
from lib.core.entities.work_managament import WMUserTypeEnum
from lib.core.jsx_conditions import EmptyQuery


logger = logging.getLogger("sa")

NotEmptyStr = constr(strict=True, min_length=1)
Expand Down Expand Up @@ -1493,10 +1492,11 @@ def get_project_steps(self, project: Union[str, dict]):
:param project: project name or metadata
:type project: str or dict

:return: project steps
:rtype: list of dicts
:return: A list of step dictionaries,
or a dictionary containing both steps and their connections (for Keypoint workflows).
:rtype: list of dicts or dict

Response Example:
Response Example for General Annotation Project:
::

[
Expand All @@ -1515,6 +1515,34 @@ def get_project_steps(self, project: Union[str, dict]):
}
]

Response Example for Keypoint Annotation Project:
::

{
"steps": [
{
"step": 1,
"className": "Left Shoulder",
"class_id": "1",
"attribute": [
{
"attribute": {
"id": 123,
"group_id": 12
}
}
]
},
{
"step": 2,
"class_id": "2",
"className": "Right Shoulder",
}
],
"connections": [
[1, 2]
]
}
"""
project_name, _ = extract_project_folder(project)
project = self.controller.get_project(project_name)
Expand Down Expand Up @@ -2511,7 +2539,12 @@ def download_export(
if response.errors:
raise AppException(response.errors)

def set_project_steps(self, project: Union[NotEmptyStr, dict], steps: List[dict]):
def set_project_steps(
self,
project: Union[NotEmptyStr, dict],
steps: List[dict],
connections: List[List[int]] = None,
):
"""Sets project's steps.

:param project: project name or metadata
Expand All @@ -2520,7 +2553,11 @@ def set_project_steps(self, project: Union[NotEmptyStr, dict], steps: List[dict]
:param steps: new workflow list of dicts
:type steps: list of dicts

Request Example:
:param connections: Defines connections between keypoint annotation steps.
Each inner list specifies a pair of step IDs indicating a connection.
:type connections: list of list

Request Example for General Annotation Project:
::

sa.set_project_steps(
Expand All @@ -2541,10 +2578,40 @@ def set_project_steps(self, project: Union[NotEmptyStr, dict], steps: List[dict]
}
]
)

Request Example for Keypoint Annotation Project:
::

sa.set_project_steps(
project="Pose Estimation Project",
steps=[
{
"step": 1,
"class_id": 12,
"attribute": [
{
"attribute": {
"id": 123,
"group_id": 12
}
}
]
},
{
"step": 2,
"class_id": 13
}
],
connections=[
[1, 2]
]
)
"""
project_name, _ = extract_project_folder(project)
project = self.controller.get_project(project_name)
response = self.controller.projects.set_steps(project, steps=steps)
response = self.controller.projects.set_steps(
project, steps=steps, connections=connections
)
if response.errors:
raise AppException(response.errors)

Expand Down
2 changes: 2 additions & 0 deletions src/superannotate/lib/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from lib.core.enums import ImageQuality
from lib.core.enums import ProjectStatus
from lib.core.enums import ProjectType
from lib.core.enums import StepsType
from lib.core.enums import TrainingStatus
from lib.core.enums import UploadState
from lib.core.enums import UserRole
Expand Down Expand Up @@ -186,6 +187,7 @@ def setup_logging(level=DEFAULT_LOGGING_LEVEL, file_path=LOG_FILE_LOCATION):
FolderStatus,
ProjectStatus,
ProjectType,
StepsType,
UserRole,
UploadState,
TrainingStatus,
Expand Down
1 change: 1 addition & 0 deletions src/superannotate/lib/core/entities/work_managament.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ class WMProjectUserEntity(TimedBaseModel):
email: Optional[str]
state: Optional[WMUserStateEnum]
custom_fields: Optional[dict] = Field(dict(), alias="customField")
permissions: Optional[dict]

class Config:
extra = Extra.ignore
Expand Down
6 changes: 6 additions & 0 deletions src/superannotate/lib/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ def images(self):
return self.VECTOR.value, self.PIXEL.value, self.TILED.value


class StepsType(Enum):
INITIAL = 1
BASIC = 2
KEYPOINT = 3


class UserRole(BaseTitledEnum):
CONTRIBUTOR = "Contributor", 4
ADMIN = "Admin", 7
Expand Down
8 changes: 8 additions & 0 deletions src/superannotate/lib/core/serviceproviders.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,18 @@ def set_settings(
def list_steps(self, project: entities.ProjectEntity):
raise NotImplementedError

@abstractmethod
def list_keypoint_steps(self, project: entities.ProjectEntity):
raise NotImplementedError

@abstractmethod
def set_step(self, project: entities.ProjectEntity, step: entities.StepEntity):
raise NotImplementedError

@abstractmethod
def set_keypoint_steps(self, project: entities.ProjectEntity, steps, connections):
raise NotImplementedError

@abstractmethod
def set_steps(self, project: entities.ProjectEntity, steps: list):
raise NotImplementedError
Expand Down
26 changes: 13 additions & 13 deletions src/superannotate/lib/core/usecases/annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ def log_report(

class ItemToUpload(BaseModel):
item: BaseItemEntity
annotation_json: Optional[dict]
path: Optional[str]
file_size: Optional[int]
mask: Optional[io.BytesIO]
annotation_json: Optional[dict] = None
path: Optional[str] = None
file_size: Optional[int] = None
mask: Optional[io.BytesIO] = None

class Config:
arbitrary_types_allowed = True
Expand Down Expand Up @@ -282,7 +282,7 @@ def validate_project_type(self):
raise AppException("Unsupported project type.")

def _validate_json(self, json_data: dict) -> list:
if self._project.type >= constants.ProjectType.PIXEL.value:
if self._project.type >= int(constants.ProjectType.PIXEL):
return []
use_case = ValidateAnnotationUseCase(
reporter=self.reporter,
Expand Down Expand Up @@ -2101,16 +2101,16 @@ def execute(self):
if categorization_enabled:
item_id_category_map = {}
for item_name in uploaded_annotations:
category = (
name_annotation_map[item_name]["metadata"]
.get("item_category", {})
.get("value")
category = name_annotation_map[item_name]["metadata"].get(
"item_category", None
)
if category:
item_id_category_map[name_item_map[item_name].id] = category
self._attach_categories(
folder_id=folder.id, item_id_category_map=item_id_category_map
)
if item_id_category_map:
self._attach_categories(
folder_id=folder.id,
item_id_category_map=item_id_category_map,
)
workflow = self._service_provider.work_management.get_workflow(
self._project.workflow_id
)
Expand Down Expand Up @@ -2149,7 +2149,7 @@ def _attach_categories(self, folder_id: int, item_id_category_map: Dict[int, str
)
response.raise_for_status()
categories = response.data
self._category_name_to_id_map = {c.name: c.id for c in categories}
self._category_name_to_id_map = {c.value: c.id for c in categories}
for item_id in list(item_id_category_map.keys()):
category_name = item_id_category_map[item_id]
if category_name not in self._category_name_to_id_map:
Expand Down
Loading
Loading