diff --git a/.mock/definition/__package__.yml b/.mock/definition/__package__.yml index 99abcd937..cfc052db9 100644 --- a/.mock/definition/__package__.yml +++ b/.mock/definition/__package__.yml @@ -253,7 +253,6 @@ types: ground_truth_number: type: integer docs: Honeypot annotation number in project - has_blueprints: string id: integer is_draft: type: optional @@ -7083,6 +7082,47 @@ types: * `RE` - Review source: openapi: openapi/openapi.yaml + TaskEvent: + docs: >- + Serializer for TaskEvent model to handle event creation from frontend. + + + This serializer validates and processes task events sent from the labeling + interface, + + ensuring proper data format and automatically setting required + relationships. + properties: + actor: integer + annotation: + type: optional + docs: Annotation ID associated with this event + annotation_draft: + type: optional + docs: Draft annotation ID associated with this event + created_at: datetime + event_key: + type: string + docs: >- + Event type identifier (e.g., "annotation_loaded", + "region_finished_drawing") + validation: + maxLength: 255 + event_time: + type: datetime + docs: Timestamp when the event occurred (frontend time) + id: integer + meta: optional + organization: integer + project: integer + review: + type: optional + docs: Review ID associated with this event + task: + type: integer + docs: Task this event is associated with + source: + openapi: openapi/openapi.yaml TaskSimple: properties: annotations: list diff --git a/.mock/definition/projects.yml b/.mock/definition/projects.yml index 164ec9183..41652e2b8 100644 --- a/.mock/definition/projects.yml +++ b/.mock/definition/projects.yml @@ -199,7 +199,6 @@ service: expert_instruction: expert_instruction finished_task_number: 1 ground_truth_number: 1 - has_blueprints: has_blueprints id: 1 is_draft: true is_published: true diff --git a/.mock/definition/prompts.yml b/.mock/definition/prompts.yml index 0e9fa15aa..822dc918b 100644 --- a/.mock/definition/prompts.yml +++ b/.mock/definition/prompts.yml @@ -234,7 +234,6 @@ service: expert_instruction: expert_instruction finished_task_number: 1 ground_truth_number: 1 - has_blueprints: has_blueprints id: 1 is_draft: true is_published: true diff --git a/.mock/definition/tasks.yml b/.mock/definition/tasks.yml index 27d494803..bc050afe2 100644 --- a/.mock/definition/tasks.yml +++ b/.mock/definition/tasks.yml @@ -546,6 +546,124 @@ service: - key: value audiences: - public + create_event: + path: /api/tasks/{id}/events/ + method: POST + auth: true + docs: |2- + + Create a new task event to track user interactions and system events during annotation. + + This endpoint is designed to receive events from the frontend labeling interface to enable + accurate lead time calculation and detailed annotation analytics. + + ## Event Types + + **Core Annotation Events:** + - `annotation_loaded` - When annotation interface is loaded + - `annotation_submitted` - When annotation is submitted + - `annotation_updated` - When annotation is modified + - `annotation_reviewed` - When annotation is reviewed + + **User Activity Events:** + - `visibility_change` - When page visibility changes (tab switch, minimize) + - `idle_detected` - When user goes idle + - `idle_resumed` - When user returns from idle + + **Interaction Events:** + - `region_finished_drawing` - When annotation region is completed + - `region_deleted` - When annotation regions are removed + - `hotkey_pressed` - When keyboard shortcuts are used + + **Media Events:** + - `video_playback_start/end` - Video playback control + - `audio_playback_start/end` - Audio playback control + - `video_scrub` - Video timeline scrubbing + + ## Usage + + Events are automatically associated with the task specified in the URL path. + The current user is automatically set as the actor. Project and organization + are derived from the task context. + + ## Example Request + + ```json + { + "event_key": "annotation_loaded", + "event_time": "2024-01-15T10:30:00Z", + "annotation": 123, + "meta": { + "annotation_count": 5, + "estimated_time": 300 + } + } + ``` + + source: + openapi: openapi/openapi.yaml + path-parameters: + id: + type: integer + docs: Task ID to associate the event with + display-name: Create task event + request: + name: TaskEventRequest + body: + properties: + annotation: + type: optional + docs: Annotation ID associated with this event + annotation_draft: + type: optional + docs: Draft annotation ID associated with this event + event_key: + type: string + docs: >- + Event type identifier (e.g., "annotation_loaded", + "region_finished_drawing") + validation: + minLength: 1 + maxLength: 255 + event_time: + type: datetime + docs: Timestamp when the event occurred (frontend time) + meta: optional + review: + type: optional + docs: Review ID associated with this event + content-type: application/json + response: + docs: '' + type: root.TaskEvent + errors: + - root.BadRequestError + - root.UnauthorizedError + - root.ForbiddenError + - root.NotFoundError + examples: + - path-parameters: + id: 1 + request: + event_key: event_key + event_time: '2024-01-15T09:30:00Z' + response: + body: + actor: 1 + annotation: 1 + annotation_draft: 1 + created_at: '2024-01-15T09:30:00Z' + event_key: event_key + event_time: '2024-01-15T09:30:00Z' + id: 1 + meta: + key: value + organization: 1 + project: 1 + review: 1 + task: 1 + audiences: + - public source: openapi: openapi/openapi.yaml types: diff --git a/.mock/openapi/openapi.yaml b/.mock/openapi/openapi.yaml index fd6350476..afc079083 100644 --- a/.mock/openapi/openapi.yaml +++ b/.mock/openapi/openapi.yaml @@ -14760,6 +14760,53 @@ paths: - public x-fern-sdk-group-name: annotations x-fern-sdk-method-name: create + /api/tasks/{id}/events/: + post: + description: "\n Create a new task event to track user interactions and system events during annotation.\n \n This endpoint is designed to receive events from the frontend labeling interface to enable\n accurate lead time calculation and detailed annotation analytics.\n \n ## Event Types\n \n **Core Annotation Events:**\n - `annotation_loaded` - When annotation interface is loaded\n - `annotation_submitted` - When annotation is submitted\n - `annotation_updated` - When annotation is modified\n - `annotation_reviewed` - When annotation is reviewed\n \n **User Activity Events:**\n - `visibility_change` - When page visibility changes (tab switch, minimize)\n - `idle_detected` - When user goes idle\n - `idle_resumed` - When user returns from idle\n \n **Interaction Events:**\n - `region_finished_drawing` - When annotation region is completed\n - `region_deleted` - When annotation regions are removed\n - `hotkey_pressed` - When keyboard shortcuts are used\n \n **Media Events:**\n - `video_playback_start/end` - Video playback control\n - `audio_playback_start/end` - Audio playback control\n - `video_scrub` - Video timeline scrubbing\n \n ## Usage\n \n Events are automatically associated with the task specified in the URL path.\n The current user is automatically set as the actor. Project and organization\n are derived from the task context.\n \n ## Example Request\n \n ```json\n {\n \"event_key\": \"annotation_loaded\",\n \"event_time\": \"2024-01-15T10:30:00Z\",\n \"annotation\": 123,\n \"meta\": {\n \"annotation_count\": 5,\n \"estimated_time\": 300\n }\n }\n ```\n " + operationId: api_tasks_events_create + parameters: + - description: Task ID to associate the event with + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/TaskEventRequest' + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/TaskEventRequest' + multipart/form-data: + schema: + $ref: '#/components/schemas/TaskEventRequest' + required: true + responses: + '201': + content: + application/json: + schema: + $ref: '#/components/schemas/TaskEvent' + description: '' + '400': + description: Bad request - validation errors + '401': + description: Unauthorized - authentication required + '403': + description: Forbidden - insufficient permissions + '404': + description: Not found - task does not exist + security: + - Token: [] + summary: Create task event + tags: + - Task Events + x-fern-audiences: + - public + x-fern-sdk-group-name: tasks + x-fern-sdk-method-name: create_event /api/token/: get: description: List all API tokens for the current user. @@ -16299,9 +16346,6 @@ components: description: Honeypot annotation number in project readOnly: true type: integer - has_blueprints: - readOnly: true - type: string id: readOnly: true type: integer @@ -16467,7 +16511,6 @@ components: - description_short - finished_task_number - ground_truth_number - - has_blueprints - id - members - members_count @@ -27627,6 +27670,101 @@ components: - id - task type: object + TaskEvent: + description: |- + Serializer for TaskEvent model to handle event creation from frontend. + + This serializer validates and processes task events sent from the labeling interface, + ensuring proper data format and automatically setting required relationships. + properties: + actor: + readOnly: true + type: integer + annotation: + description: Annotation ID associated with this event + nullable: true + type: integer + annotation_draft: + description: Draft annotation ID associated with this event + nullable: true + type: integer + created_at: + format: date-time + readOnly: true + type: string + event_key: + description: Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + maxLength: 255 + type: string + event_time: + description: Timestamp when the event occurred (frontend time) + format: date-time + type: string + id: + readOnly: true + type: integer + meta: + description: Additional event metadata (region data, hotkey info, etc.) + nullable: true + organization: + readOnly: true + type: integer + project: + readOnly: true + type: integer + review: + description: Review ID associated with this event + nullable: true + type: integer + task: + description: Task this event is associated with + readOnly: true + type: integer + required: + - actor + - created_at + - event_key + - event_time + - id + - organization + - project + - task + type: object + TaskEventRequest: + description: |- + Serializer for TaskEvent model to handle event creation from frontend. + + This serializer validates and processes task events sent from the labeling interface, + ensuring proper data format and automatically setting required relationships. + properties: + annotation: + description: Annotation ID associated with this event + nullable: true + type: integer + annotation_draft: + description: Draft annotation ID associated with this event + nullable: true + type: integer + event_key: + description: Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + maxLength: 255 + minLength: 1 + type: string + event_time: + description: Timestamp when the event occurred (frontend time) + format: date-time + type: string + meta: + description: Additional event metadata (region data, hotkey info, etc.) + nullable: true + review: + description: Review ID associated with this event + nullable: true + type: integer + required: + - event_key + - event_time + type: object TaskSimple: properties: annotations: diff --git a/reference.md b/reference.md index 1c3408e0e..a54f6d6b4 100644 --- a/reference.md +++ b/reference.md @@ -8743,6 +8743,178 @@ client.tasks.update( + + + + +
client.tasks.create_event(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ + + Create a new task event to track user interactions and system events during annotation. + + This endpoint is designed to receive events from the frontend labeling interface to enable + accurate lead time calculation and detailed annotation analytics. + + ## Event Types + + **Core Annotation Events:** + - `annotation_loaded` - When annotation interface is loaded + - `annotation_submitted` - When annotation is submitted + - `annotation_updated` - When annotation is modified + - `annotation_reviewed` - When annotation is reviewed + + **User Activity Events:** + - `visibility_change` - When page visibility changes (tab switch, minimize) + - `idle_detected` - When user goes idle + - `idle_resumed` - When user returns from idle + + **Interaction Events:** + - `region_finished_drawing` - When annotation region is completed + - `region_deleted` - When annotation regions are removed + - `hotkey_pressed` - When keyboard shortcuts are used + + **Media Events:** + - `video_playback_start/end` - Video playback control + - `audio_playback_start/end` - Audio playback control + - `video_scrub` - Video timeline scrubbing + + ## Usage + + Events are automatically associated with the task specified in the URL path. + The current user is automatically set as the actor. Project and organization + are derived from the task context. + + ## Example Request + + ```json + { + "event_key": "annotation_loaded", + "event_time": "2024-01-15T10:30:00Z", + "annotation": 123, + "meta": { + "annotation_count": 5, + "estimated_time": 300 + } + } + ``` + +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +import datetime + +from label_studio_sdk import LabelStudio + +client = LabelStudio( + api_key="YOUR_API_KEY", +) +client.tasks.create_event( + id=1, + event_key="event_key", + event_time=datetime.datetime.fromisoformat( + "2024-01-15 09:30:00+00:00", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `int` — Task ID to associate the event with + +
+
+ +
+
+ +**event_key:** `str` — Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + +
+
+ +
+
+ +**event_time:** `dt.datetime` — Timestamp when the event occurred (frontend time) + +
+
+ +
+
+ +**annotation:** `typing.Optional[int]` — Annotation ID associated with this event + +
+
+ +
+
+ +**annotation_draft:** `typing.Optional[int]` — Draft annotation ID associated with this event + +
+
+ +
+
+ +**meta:** `typing.Optional[typing.Optional[typing.Any]]` + +
+
+ +
+
+ +**review:** `typing.Optional[int]` — Review ID associated with this event + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/src/label_studio_sdk/__init__.py b/src/label_studio_sdk/__init__.py index 2ed0410ed..b31cd1eb0 100644 --- a/src/label_studio_sdk/__init__.py +++ b/src/label_studio_sdk/__init__.py @@ -176,6 +176,7 @@ Status7BfEnum, StatusC5AEnum, TaskAssignment, + TaskEvent, ThirdPartyModelVersion, ThirdPartyModelVersionRequest, TokenRefreshResponse, @@ -518,6 +519,7 @@ "Status7BfEnum", "StatusC5AEnum", "TaskAssignment", + "TaskEvent", "TasksListRequestFields", "ThirdPartyModelVersion", "ThirdPartyModelVersionRequest", diff --git a/src/label_studio_sdk/tasks/client.py b/src/label_studio_sdk/tasks/client.py index 0583af903..1c50065ee 100644 --- a/src/label_studio_sdk/tasks/client.py +++ b/src/label_studio_sdk/tasks/client.py @@ -17,6 +17,8 @@ from ..errors.forbidden_error import ForbiddenError import datetime as dt from ..types.lse_task import LseTask +from ..types.task_event import TaskEvent +from ..errors.not_found_error import NotFoundError from ..core.client_wrapper import AsyncClientWrapper from ..core.pagination import AsyncPager @@ -644,6 +646,187 @@ def update( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def create_event( + self, + id: int, + *, + event_key: str, + event_time: dt.datetime, + annotation: typing.Optional[int] = OMIT, + annotation_draft: typing.Optional[int] = OMIT, + meta: typing.Optional[typing.Optional[typing.Any]] = OMIT, + review: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TaskEvent: + """ + + Create a new task event to track user interactions and system events during annotation. + + This endpoint is designed to receive events from the frontend labeling interface to enable + accurate lead time calculation and detailed annotation analytics. + + ## Event Types + + **Core Annotation Events:** + - `annotation_loaded` - When annotation interface is loaded + - `annotation_submitted` - When annotation is submitted + - `annotation_updated` - When annotation is modified + - `annotation_reviewed` - When annotation is reviewed + + **User Activity Events:** + - `visibility_change` - When page visibility changes (tab switch, minimize) + - `idle_detected` - When user goes idle + - `idle_resumed` - When user returns from idle + + **Interaction Events:** + - `region_finished_drawing` - When annotation region is completed + - `region_deleted` - When annotation regions are removed + - `hotkey_pressed` - When keyboard shortcuts are used + + **Media Events:** + - `video_playback_start/end` - Video playback control + - `audio_playback_start/end` - Audio playback control + - `video_scrub` - Video timeline scrubbing + + ## Usage + + Events are automatically associated with the task specified in the URL path. + The current user is automatically set as the actor. Project and organization + are derived from the task context. + + ## Example Request + + ```json + { + "event_key": "annotation_loaded", + "event_time": "2024-01-15T10:30:00Z", + "annotation": 123, + "meta": { + "annotation_count": 5, + "estimated_time": 300 + } + } + ``` + + + Parameters + ---------- + id : int + Task ID to associate the event with + + event_key : str + Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + + event_time : dt.datetime + Timestamp when the event occurred (frontend time) + + annotation : typing.Optional[int] + Annotation ID associated with this event + + annotation_draft : typing.Optional[int] + Draft annotation ID associated with this event + + meta : typing.Optional[typing.Optional[typing.Any]] + + review : typing.Optional[int] + Review ID associated with this event + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TaskEvent + + + Examples + -------- + import datetime + + from label_studio_sdk import LabelStudio + + client = LabelStudio( + api_key="YOUR_API_KEY", + ) + client.tasks.create_event( + id=1, + event_key="event_key", + event_time=datetime.datetime.fromisoformat( + "2024-01-15 09:30:00+00:00", + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"api/tasks/{jsonable_encoder(id)}/events/", + method="POST", + json={ + "annotation": annotation, + "annotation_draft": annotation_draft, + "event_key": event_key, + "event_time": event_time, + "meta": meta, + "review": review, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + TaskEvent, + construct_type( + type_=TaskEvent, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 401: + raise UnauthorizedError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + class AsyncTasksClient: def __init__(self, *, client_wrapper: AsyncClientWrapper): @@ -1320,3 +1503,191 @@ async def main() -> None: except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + + async def create_event( + self, + id: int, + *, + event_key: str, + event_time: dt.datetime, + annotation: typing.Optional[int] = OMIT, + annotation_draft: typing.Optional[int] = OMIT, + meta: typing.Optional[typing.Optional[typing.Any]] = OMIT, + review: typing.Optional[int] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> TaskEvent: + """ + + Create a new task event to track user interactions and system events during annotation. + + This endpoint is designed to receive events from the frontend labeling interface to enable + accurate lead time calculation and detailed annotation analytics. + + ## Event Types + + **Core Annotation Events:** + - `annotation_loaded` - When annotation interface is loaded + - `annotation_submitted` - When annotation is submitted + - `annotation_updated` - When annotation is modified + - `annotation_reviewed` - When annotation is reviewed + + **User Activity Events:** + - `visibility_change` - When page visibility changes (tab switch, minimize) + - `idle_detected` - When user goes idle + - `idle_resumed` - When user returns from idle + + **Interaction Events:** + - `region_finished_drawing` - When annotation region is completed + - `region_deleted` - When annotation regions are removed + - `hotkey_pressed` - When keyboard shortcuts are used + + **Media Events:** + - `video_playback_start/end` - Video playback control + - `audio_playback_start/end` - Audio playback control + - `video_scrub` - Video timeline scrubbing + + ## Usage + + Events are automatically associated with the task specified in the URL path. + The current user is automatically set as the actor. Project and organization + are derived from the task context. + + ## Example Request + + ```json + { + "event_key": "annotation_loaded", + "event_time": "2024-01-15T10:30:00Z", + "annotation": 123, + "meta": { + "annotation_count": 5, + "estimated_time": 300 + } + } + ``` + + + Parameters + ---------- + id : int + Task ID to associate the event with + + event_key : str + Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + + event_time : dt.datetime + Timestamp when the event occurred (frontend time) + + annotation : typing.Optional[int] + Annotation ID associated with this event + + annotation_draft : typing.Optional[int] + Draft annotation ID associated with this event + + meta : typing.Optional[typing.Optional[typing.Any]] + + review : typing.Optional[int] + Review ID associated with this event + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + TaskEvent + + + Examples + -------- + import asyncio + import datetime + + from label_studio_sdk import AsyncLabelStudio + + client = AsyncLabelStudio( + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.tasks.create_event( + id=1, + event_key="event_key", + event_time=datetime.datetime.fromisoformat( + "2024-01-15 09:30:00+00:00", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"api/tasks/{jsonable_encoder(id)}/events/", + method="POST", + json={ + "annotation": annotation, + "annotation_draft": annotation_draft, + "event_key": event_key, + "event_time": event_time, + "meta": meta, + "review": review, + }, + headers={ + "content-type": "application/json", + }, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return typing.cast( + TaskEvent, + construct_type( + type_=TaskEvent, # type: ignore + object_=_response.json(), + ), + ) + if _response.status_code == 400: + raise BadRequestError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 401: + raise UnauthorizedError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 403: + raise ForbiddenError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + if _response.status_code == 404: + raise NotFoundError( + typing.cast( + typing.Optional[typing.Any], + construct_type( + type_=typing.Optional[typing.Any], # type: ignore + object_=_response.json(), + ), + ) + ) + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) diff --git a/src/label_studio_sdk/types/__init__.py b/src/label_studio_sdk/types/__init__.py index 4b47e1323..c28817b32 100644 --- a/src/label_studio_sdk/types/__init__.py +++ b/src/label_studio_sdk/types/__init__.py @@ -177,6 +177,7 @@ from .status7bf_enum import Status7BfEnum from .status_c5a_enum import StatusC5AEnum from .task_assignment import TaskAssignment +from .task_event import TaskEvent from .third_party_model_version import ThirdPartyModelVersion from .third_party_model_version_request import ThirdPartyModelVersionRequest from .token_refresh_response import TokenRefreshResponse @@ -373,6 +374,7 @@ "Status7BfEnum", "StatusC5AEnum", "TaskAssignment", + "TaskEvent", "ThirdPartyModelVersion", "ThirdPartyModelVersionRequest", "TokenRefreshResponse", diff --git a/src/label_studio_sdk/types/all_roles_project_list.py b/src/label_studio_sdk/types/all_roles_project_list.py index c229db31f..2d2dea964 100644 --- a/src/label_studio_sdk/types/all_roles_project_list.py +++ b/src/label_studio_sdk/types/all_roles_project_list.py @@ -81,7 +81,6 @@ class AllRolesProjectList(UncheckedBaseModel): Honeypot annotation number in project """ - has_blueprints: str id: int is_draft: typing.Optional[bool] = pydantic.Field(default=None) """ diff --git a/src/label_studio_sdk/types/task_event.py b/src/label_studio_sdk/types/task_event.py new file mode 100644 index 000000000..6a56265d0 --- /dev/null +++ b/src/label_studio_sdk/types/task_event.py @@ -0,0 +1,61 @@ +# This file was auto-generated by Fern from our API Definition. + +from ..core.unchecked_base_model import UncheckedBaseModel +import typing +import pydantic +import datetime as dt +from ..core.pydantic_utilities import IS_PYDANTIC_V2 + + +class TaskEvent(UncheckedBaseModel): + """ + Serializer for TaskEvent model to handle event creation from frontend. + + This serializer validates and processes task events sent from the labeling interface, + ensuring proper data format and automatically setting required relationships. + """ + + actor: int + annotation: typing.Optional[int] = pydantic.Field(default=None) + """ + Annotation ID associated with this event + """ + + annotation_draft: typing.Optional[int] = pydantic.Field(default=None) + """ + Draft annotation ID associated with this event + """ + + created_at: dt.datetime + event_key: str = pydantic.Field() + """ + Event type identifier (e.g., "annotation_loaded", "region_finished_drawing") + """ + + event_time: dt.datetime = pydantic.Field() + """ + Timestamp when the event occurred (frontend time) + """ + + id: int + meta: typing.Optional[typing.Optional[typing.Any]] = None + organization: int + project: int + review: typing.Optional[int] = pydantic.Field(default=None) + """ + Review ID associated with this event + """ + + task: int = pydantic.Field() + """ + Task this event is associated with + """ + + if IS_PYDANTIC_V2: + model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2 + else: + + class Config: + frozen = True + smart_union = True + extra = pydantic.Extra.allow diff --git a/tests/test_prompts.py b/tests/test_prompts.py index bb39ee8f6..d64449c92 100644 --- a/tests/test_prompts.py +++ b/tests/test_prompts.py @@ -148,7 +148,6 @@ async def test_compatible_projects(client: LabelStudio, async_client: AsyncLabel "expert_instruction": "expert_instruction", "finished_task_number": 1, "ground_truth_number": 1, - "has_blueprints": "has_blueprints", "id": 1, "is_draft": True, "is_published": True, @@ -234,7 +233,6 @@ async def test_compatible_projects(client: LabelStudio, async_client: AsyncLabel "expert_instruction": None, "finished_task_number": "integer", "ground_truth_number": "integer", - "has_blueprints": None, "id": "integer", "is_draft": None, "is_published": None, diff --git a/tests/test_tasks.py b/tests/test_tasks.py index af29d5a2c..6192c26ca 100644 --- a/tests/test_tasks.py +++ b/tests/test_tasks.py @@ -4,6 +4,7 @@ from label_studio_sdk import AsyncLabelStudio import typing from .utilities import validate_response +import datetime async def test_create_many_status(client: LabelStudio, async_client: AsyncLabelStudio) -> None: @@ -516,3 +517,43 @@ async def test_update(client: LabelStudio, async_client: AsyncLabelStudio) -> No async_response = await async_client.tasks.update(id="id") validate_response(async_response, expected_response, expected_types) + + +async def test_create_event(client: LabelStudio, async_client: AsyncLabelStudio) -> None: + expected_response: typing.Any = { + "actor": 1, + "annotation": 1, + "annotation_draft": 1, + "created_at": "2024-01-15T09:30:00Z", + "event_key": "event_key", + "event_time": "2024-01-15T09:30:00Z", + "id": 1, + "meta": {"key": "value"}, + "organization": 1, + "project": 1, + "review": 1, + "task": 1, + } + expected_types: typing.Any = { + "actor": "integer", + "annotation": "integer", + "annotation_draft": "integer", + "created_at": "datetime", + "event_key": None, + "event_time": "datetime", + "id": "integer", + "meta": None, + "organization": "integer", + "project": "integer", + "review": "integer", + "task": "integer", + } + response = client.tasks.create_event( + id=1, event_key="event_key", event_time=datetime.datetime.fromisoformat("2024-01-15 09:30:00+00:00") + ) + validate_response(response, expected_response, expected_types) + + async_response = await async_client.tasks.create_event( + id=1, event_key="event_key", event_time=datetime.datetime.fromisoformat("2024-01-15 09:30:00+00:00") + ) + validate_response(async_response, expected_response, expected_types)