synapse-sdk 1.0.0a59__py3-none-any.whl → 1.0.0a61__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of synapse-sdk might be problematic. Click here for more details.
- synapse_sdk/cli/__init__.py +246 -5
- synapse_sdk/cli/alias/utils.py +1 -1
- synapse_sdk/cli/config.py +339 -0
- synapse_sdk/cli/devtools.py +61 -0
- synapse_sdk/cli/plugin/publish.py +3 -4
- synapse_sdk/clients/agent/__init__.py +7 -2
- synapse_sdk/clients/agent/ray.py +37 -6
- synapse_sdk/clients/backend/__init__.py +5 -9
- synapse_sdk/clients/backend/annotation.py +4 -0
- synapse_sdk/clients/base.py +42 -3
- synapse_sdk/devtools/__init__.py +0 -0
- synapse_sdk/devtools/config.py +94 -0
- synapse_sdk/devtools/docs/.gitignore +20 -0
- synapse_sdk/devtools/docs/README.md +41 -0
- synapse_sdk/devtools/docs/blog/2019-05-28-first-blog-post.md +12 -0
- synapse_sdk/devtools/docs/blog/2019-05-29-long-blog-post.md +44 -0
- synapse_sdk/devtools/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- synapse_sdk/devtools/docs/blog/2021-08-26-welcome/index.md +29 -0
- synapse_sdk/devtools/docs/blog/authors.yml +25 -0
- synapse_sdk/devtools/docs/blog/tags.yml +19 -0
- synapse_sdk/devtools/docs/docusaurus.config.ts +138 -0
- synapse_sdk/devtools/docs/package-lock.json +17455 -0
- synapse_sdk/devtools/docs/package.json +47 -0
- synapse_sdk/devtools/docs/sidebars.ts +36 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/index.tsx +71 -0
- synapse_sdk/devtools/docs/src/components/HomepageFeatures/styles.module.css +11 -0
- synapse_sdk/devtools/docs/src/css/custom.css +30 -0
- synapse_sdk/devtools/docs/src/pages/index.module.css +23 -0
- synapse_sdk/devtools/docs/src/pages/index.tsx +21 -0
- synapse_sdk/devtools/docs/src/pages/markdown-page.md +7 -0
- synapse_sdk/devtools/docs/static/.nojekyll +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus-social-card.jpg +0 -0
- synapse_sdk/devtools/docs/static/img/docusaurus.png +0 -0
- synapse_sdk/devtools/docs/static/img/favicon.ico +0 -0
- synapse_sdk/devtools/docs/static/img/logo.png +0 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_react.svg +170 -0
- synapse_sdk/devtools/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- synapse_sdk/devtools/docs/tsconfig.json +8 -0
- synapse_sdk/devtools/models.py +55 -0
- synapse_sdk/devtools/server.py +829 -0
- synapse_sdk/devtools/web/.gitignore +2 -0
- synapse_sdk/devtools/web/README.md +34 -0
- synapse_sdk/devtools/web/dist/index.html +17 -0
- synapse_sdk/devtools/web/index.html +16 -0
- synapse_sdk/devtools/web/jsconfig.json +15 -0
- synapse_sdk/devtools/web/package-lock.json +2609 -0
- synapse_sdk/devtools/web/package.json +27 -0
- synapse_sdk/devtools/web/pnpm-lock.yaml +1055 -0
- synapse_sdk/devtools/web/src/App.jsx +14 -0
- synapse_sdk/devtools/web/src/App.module.css +33 -0
- synapse_sdk/devtools/web/src/assets/favicon.ico +0 -0
- synapse_sdk/devtools/web/src/components/Breadcrumbs.jsx +42 -0
- synapse_sdk/devtools/web/src/components/Layout.jsx +12 -0
- synapse_sdk/devtools/web/src/components/LogViewer.jsx +266 -0
- synapse_sdk/devtools/web/src/components/MessageViewer.jsx +150 -0
- synapse_sdk/devtools/web/src/components/NavigationSidebar.jsx +137 -0
- synapse_sdk/devtools/web/src/components/ServerStatusBar.jsx +245 -0
- synapse_sdk/devtools/web/src/components/icons.jsx +325 -0
- synapse_sdk/devtools/web/src/index.css +470 -0
- synapse_sdk/devtools/web/src/index.jsx +15 -0
- synapse_sdk/devtools/web/src/logo.svg +1 -0
- synapse_sdk/devtools/web/src/router.jsx +34 -0
- synapse_sdk/devtools/web/src/utils/api.js +425 -0
- synapse_sdk/devtools/web/src/views/ApplicationDetailView.jsx +241 -0
- synapse_sdk/devtools/web/src/views/ApplicationsView.jsx +224 -0
- synapse_sdk/devtools/web/src/views/HomeView.jsx +197 -0
- synapse_sdk/devtools/web/src/views/JobDetailView.jsx +310 -0
- synapse_sdk/devtools/web/src/views/PluginView.jsx +914 -0
- synapse_sdk/devtools/web/vite.config.js +13 -0
- synapse_sdk/plugins/categories/neural_net/actions/tune.py +1 -1
- synapse_sdk/plugins/categories/neural_net/templates/config.yaml +15 -3
- synapse_sdk/plugins/categories/neural_net/templates/plugin/inference.py +26 -10
- synapse_sdk/plugins/categories/pre_annotation/actions/to_task.py +236 -64
- synapse_sdk/plugins/categories/pre_annotation/templates/config.yaml +14 -2
- synapse_sdk/plugins/templates/plugin-config-schema.json +409 -0
- synapse_sdk/plugins/templates/schema.json +484 -0
- synapse_sdk/utils/converters/__init__.py +145 -0
- synapse_sdk/utils/converters/coco/__init__.py +0 -0
- synapse_sdk/utils/converters/coco/from_dm.py +269 -0
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/METADATA +9 -22
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/RECORD +87 -19
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/WHEEL +0 -0
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/entry_points.txt +0 -0
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/licenses/LICENSE +0 -0
- {synapse_sdk-1.0.0a59.dist-info → synapse_sdk-1.0.0a61.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
3
|
+
import solidPlugin from "vite-plugin-solid";
|
|
4
|
+
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [tailwindcss(), solidPlugin()],
|
|
7
|
+
server: {
|
|
8
|
+
port: 3000,
|
|
9
|
+
},
|
|
10
|
+
build: {
|
|
11
|
+
target: "esnext",
|
|
12
|
+
},
|
|
13
|
+
});
|
|
@@ -260,10 +260,10 @@ class TuneAction(TrainAction):
|
|
|
260
260
|
|
|
261
261
|
from ray.tune.schedulers import (
|
|
262
262
|
ASHAScheduler,
|
|
263
|
+
FIFOScheduler,
|
|
263
264
|
HyperBandScheduler,
|
|
264
265
|
MedianStoppingRule,
|
|
265
266
|
PopulationBasedTraining,
|
|
266
|
-
FIFOScheduler,
|
|
267
267
|
)
|
|
268
268
|
|
|
269
269
|
if tune_config.get('scheduler') is None:
|
|
@@ -13,12 +13,24 @@ actions:
|
|
|
13
13
|
dataset: dataset
|
|
14
14
|
entrypoint: plugin.train.train
|
|
15
15
|
metrics:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
train:
|
|
17
|
+
epoch:
|
|
18
|
+
loss: # Use the plugin's internal variable as the key, and the user-facing title in name.
|
|
19
|
+
name: Loss
|
|
20
|
+
validation:
|
|
21
|
+
epoch:
|
|
22
|
+
acc:
|
|
23
|
+
name: Accuracy
|
|
19
24
|
hyperparameters:
|
|
20
25
|
ui_schema: |
|
|
21
26
|
Dumped FormKit Schema for hyperparameters
|
|
27
|
+
visualizations:
|
|
28
|
+
validation_samples_per_epochs: # put in log_visualization name
|
|
29
|
+
type: vis_type
|
|
30
|
+
name: user-facing title
|
|
31
|
+
options:
|
|
32
|
+
group_name: Epoch
|
|
33
|
+
thumbnail_size: [50, 50]
|
|
22
34
|
options:
|
|
23
35
|
visualize: false # Whether to visualize the training process
|
|
24
36
|
deployment:
|
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import BaseModel
|
|
2
2
|
|
|
3
|
+
# for load file with synapse
|
|
4
|
+
# from synapse_sdk.types import FileField
|
|
5
|
+
from synapse_sdk.plugins.categories.neural_net.base.inference import BaseInference, app
|
|
3
6
|
|
|
4
|
-
class MockNetInference:
|
|
5
|
-
model_id = None
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
return model_id
|
|
8
|
+
class InputData(BaseModel): # Pydantic
|
|
9
|
+
input_string: str
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
|
|
12
|
+
class ResNetInference(BaseInference):
|
|
13
|
+
async def _get_model(self, model): # Load model
|
|
14
|
+
model_directory_path = model['path']
|
|
15
|
+
|
|
16
|
+
# implement model_load code
|
|
17
|
+
model = model_directory_path
|
|
18
|
+
|
|
19
|
+
return model # return loaded_model
|
|
20
|
+
|
|
21
|
+
@app.post('/load_model')
|
|
22
|
+
async def load_model(self):
|
|
23
|
+
await self.get_model()
|
|
24
|
+
|
|
25
|
+
@app.post('/')
|
|
26
|
+
async def infer(self, data: InputData):
|
|
27
|
+
model = await self.get_model()
|
|
28
|
+
results = model(data.input_string) # This is Sample code. implement your model's prediction code
|
|
29
|
+
|
|
30
|
+
return results
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from enum import Enum
|
|
4
|
-
from typing import Annotated
|
|
4
|
+
from typing import Annotated, Any, Dict, List, Optional
|
|
5
5
|
|
|
6
6
|
import requests
|
|
7
7
|
from pydantic import AfterValidator, BaseModel, field_validator
|
|
8
8
|
from pydantic_core import PydanticCustomError
|
|
9
9
|
|
|
10
|
+
from synapse_sdk.clients.backend import BackendClient
|
|
10
11
|
from synapse_sdk.clients.exceptions import ClientError
|
|
11
12
|
from synapse_sdk.plugins.categories.base import Action
|
|
12
13
|
from synapse_sdk.plugins.categories.decorators import register_action
|
|
@@ -29,7 +30,7 @@ class ToTaskRun(Run):
|
|
|
29
30
|
class AnnotateTaskDataLog(BaseModel):
|
|
30
31
|
"""Log model for annotate task data."""
|
|
31
32
|
|
|
32
|
-
task_info: str
|
|
33
|
+
task_info: Optional[str] = None
|
|
33
34
|
status: AnnotateTaskDataStatus
|
|
34
35
|
created: str
|
|
35
36
|
|
|
@@ -40,7 +41,7 @@ class ToTaskRun(Run):
|
|
|
40
41
|
failed: int
|
|
41
42
|
success: int
|
|
42
43
|
|
|
43
|
-
def log_annotate_task_data(self, task_info:
|
|
44
|
+
def log_annotate_task_data(self, task_info: Dict[str, Any], status: AnnotateTaskDataStatus):
|
|
44
45
|
"""Log annotate task data."""
|
|
45
46
|
now = datetime.now().isoformat()
|
|
46
47
|
self.log(
|
|
@@ -56,7 +57,7 @@ class ToTaskRun(Run):
|
|
|
56
57
|
category (str): The category of the metrics.
|
|
57
58
|
"""
|
|
58
59
|
record = self.MetricsRecord.model_validate(record)
|
|
59
|
-
self.set_metrics(value=record.
|
|
60
|
+
self.set_metrics(value=record.model_dump(), category=category)
|
|
60
61
|
|
|
61
62
|
|
|
62
63
|
class ToTaskParams(BaseModel):
|
|
@@ -66,7 +67,7 @@ class ToTaskParams(BaseModel):
|
|
|
66
67
|
name (str): The name of the action.
|
|
67
68
|
description (str | None): The description of the action.
|
|
68
69
|
project (int): The project ID.
|
|
69
|
-
|
|
70
|
+
task_filters (dict): The filters of tasks.
|
|
70
71
|
method (AnnotationMethod): The method of annotation.
|
|
71
72
|
target_specification_name (str | None): The name of the target specification.
|
|
72
73
|
pre_processor (int | None): The pre processor ID.
|
|
@@ -74,17 +75,17 @@ class ToTaskParams(BaseModel):
|
|
|
74
75
|
"""
|
|
75
76
|
|
|
76
77
|
name: Annotated[str, AfterValidator(non_blank)]
|
|
77
|
-
description: str
|
|
78
|
+
description: Optional[str] = None
|
|
78
79
|
project: int
|
|
79
|
-
|
|
80
|
-
method: AnnotationMethod
|
|
81
|
-
target_specification_name: str
|
|
82
|
-
pre_processor: int
|
|
83
|
-
pre_processor_params:
|
|
80
|
+
task_filters: Dict[str, Any]
|
|
81
|
+
method: Optional[AnnotationMethod] = None
|
|
82
|
+
target_specification_name: Optional[str] = None
|
|
83
|
+
pre_processor: Optional[int] = None
|
|
84
|
+
pre_processor_params: Dict[str, Any]
|
|
84
85
|
|
|
85
86
|
@field_validator('project', mode='before')
|
|
86
87
|
@classmethod
|
|
87
|
-
def check_project_exists(cls, value:
|
|
88
|
+
def check_project_exists(cls, value: int, info) -> int:
|
|
88
89
|
"""Validate synapse-backend project exists."""
|
|
89
90
|
if not value:
|
|
90
91
|
return value
|
|
@@ -100,15 +101,42 @@ class ToTaskParams(BaseModel):
|
|
|
100
101
|
|
|
101
102
|
@register_action
|
|
102
103
|
class ToTaskAction(Action):
|
|
103
|
-
"""ToTask action
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
"""ToTask action for pre-annotation data processing.
|
|
105
|
+
|
|
106
|
+
This action handles the process of annotating data to tasks in a project. It supports
|
|
107
|
+
two annotation methods: file-based annotation and inference-based annotation.
|
|
108
|
+
|
|
109
|
+
File-based annotation fetches data from file URLs specified in task data units,
|
|
110
|
+
downloads and processes JSON data, and updates task data with the processed information.
|
|
111
|
+
It also validates target specification names against file specifications.
|
|
112
|
+
|
|
113
|
+
Inference-based annotation is currently not supported but will support model inference
|
|
114
|
+
for automatic data annotation in future implementations.
|
|
115
|
+
|
|
116
|
+
Attrs:
|
|
117
|
+
name (str): Action name, set to 'to_task'.
|
|
118
|
+
category (PluginCategory): Plugin category, set to PRE_ANNOTATION.
|
|
119
|
+
method (RunMethod): Execution method, set to JOB.
|
|
120
|
+
run_class (Type[ToTaskRun]): Run class for this action.
|
|
121
|
+
params_model (Type[ToTaskParams]): Parameter validation model.
|
|
122
|
+
progress_categories (Dict): Progress tracking configuration.
|
|
123
|
+
metrics_categories (Set[str]): Metrics categories for this action.
|
|
124
|
+
|
|
125
|
+
Note:
|
|
126
|
+
This action requires a valid project with an associated data collection.
|
|
127
|
+
For file-based annotation, the target_specification_name must exist in the
|
|
128
|
+
project's file specifications.
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
ValueError: If run instance or parameters are not properly initialized.
|
|
132
|
+
ClientError: If there are issues with backend API calls.
|
|
106
133
|
"""
|
|
107
134
|
|
|
108
135
|
name = 'to_task'
|
|
109
136
|
category = PluginCategory.PRE_ANNOTATION
|
|
110
137
|
method = RunMethod.JOB
|
|
111
138
|
run_class = ToTaskRun
|
|
139
|
+
params_model = ToTaskParams
|
|
112
140
|
progress_categories = {
|
|
113
141
|
'annotate_task_data': {
|
|
114
142
|
'proportion': 100,
|
|
@@ -117,94 +145,238 @@ class ToTaskAction(Action):
|
|
|
117
145
|
metrics_categories = {'annotate_task_data'}
|
|
118
146
|
|
|
119
147
|
def start(self):
|
|
120
|
-
"""Start
|
|
148
|
+
"""Start to_task action.
|
|
121
149
|
|
|
122
150
|
* Generate tasks.
|
|
123
151
|
* Annotate data to tasks.
|
|
124
152
|
"""
|
|
153
|
+
if not self.run or not self.params:
|
|
154
|
+
raise ValueError('Run instance or parameters not properly initialized')
|
|
155
|
+
|
|
156
|
+
# Type assertion to help the linter
|
|
157
|
+
assert isinstance(self.run, ToTaskRun)
|
|
158
|
+
assert isinstance(self.run.client, BackendClient)
|
|
159
|
+
|
|
160
|
+
client: BackendClient = self.run.client
|
|
161
|
+
project_id = self.params['project']
|
|
162
|
+
project_response = client.get_project(project_id)
|
|
163
|
+
if isinstance(project_response, str):
|
|
164
|
+
self.run.log_message('Invalid project response received.')
|
|
165
|
+
self.run.end_log()
|
|
166
|
+
return {}
|
|
167
|
+
project: Dict[str, Any] = project_response
|
|
168
|
+
|
|
169
|
+
data_collection_id = project.get('data_collection')
|
|
170
|
+
if not data_collection_id:
|
|
171
|
+
self.run.log_message('Project does not have a data collection.')
|
|
172
|
+
self.run.end_log()
|
|
173
|
+
return {}
|
|
125
174
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
175
|
+
data_collection_response = client.get_data_collection(data_collection_id)
|
|
176
|
+
if isinstance(data_collection_response, str):
|
|
177
|
+
self.run.log_message('Invalid data collection response received.')
|
|
178
|
+
self.run.end_log()
|
|
179
|
+
return {}
|
|
180
|
+
data_collection: Dict[str, Any] = data_collection_response
|
|
132
181
|
|
|
133
182
|
# Generate tasks if provided project is empty.
|
|
134
183
|
task_ids_query_params = {
|
|
135
184
|
'project': self.params['project'],
|
|
136
185
|
'fields': 'id',
|
|
137
186
|
}
|
|
138
|
-
if self.params
|
|
139
|
-
task_ids_query_params.update(self.params['
|
|
187
|
+
if self.params['task_filters']:
|
|
188
|
+
task_ids_query_params.update(self.params['task_filters'])
|
|
140
189
|
task_ids_generator, task_ids_count = client.list_tasks(params=task_ids_query_params, list_all=True)
|
|
141
|
-
task_ids = [item
|
|
190
|
+
task_ids = [int(item.get('id', 0)) for item in task_ids_generator if isinstance(item, dict) and item.get('id')]
|
|
142
191
|
|
|
143
192
|
# If no tasks found, break the job.
|
|
144
193
|
if not task_ids_count:
|
|
145
194
|
self.run.log_message('Tasks to annotate not found.')
|
|
146
195
|
self.run.end_log()
|
|
196
|
+
return {}
|
|
147
197
|
|
|
148
198
|
# Annotate data to tasks.
|
|
149
|
-
|
|
150
|
-
if
|
|
151
|
-
|
|
152
|
-
|
|
199
|
+
method = self.params.get('method')
|
|
200
|
+
if method == AnnotationMethod.FILE:
|
|
201
|
+
# Check if target specification name exists in file specifications.
|
|
202
|
+
target_specification_name = self.params.get('target_specification_name')
|
|
203
|
+
if not target_specification_name:
|
|
204
|
+
self.run.log_message('Target specification name is required for file annotation method.')
|
|
205
|
+
self.run.end_log()
|
|
206
|
+
return {}
|
|
207
|
+
|
|
208
|
+
file_specifications = data_collection.get('file_specifications', [])
|
|
209
|
+
target_spec_exists = any(spec.get('name') == target_specification_name for spec in file_specifications)
|
|
210
|
+
if not target_spec_exists:
|
|
211
|
+
self.run.log_message(
|
|
212
|
+
f'Target specification name "{target_specification_name}" not found in file specifications'
|
|
213
|
+
)
|
|
214
|
+
self.run.end_log()
|
|
215
|
+
return {}
|
|
216
|
+
self._handle_annotate_data_from_files(task_ids, target_specification_name)
|
|
217
|
+
elif method == AnnotationMethod.INFERENCE:
|
|
153
218
|
self._handle_annotate_data_with_inference(task_ids)
|
|
219
|
+
else:
|
|
220
|
+
self.run.log_message(f'Unsupported annotation method: {method}')
|
|
221
|
+
self.run.end_log()
|
|
222
|
+
return {}
|
|
154
223
|
|
|
155
224
|
return {}
|
|
156
225
|
|
|
157
|
-
def _handle_annotate_data_from_files(self, task_ids:
|
|
226
|
+
def _handle_annotate_data_from_files(self, task_ids: List[int], target_specification_name: str):
|
|
158
227
|
"""Handle annotate data from files to tasks.
|
|
159
228
|
|
|
160
229
|
Args:
|
|
161
|
-
task_ids (
|
|
230
|
+
task_ids (List[int]): List of task IDs to annotate data to.
|
|
231
|
+
target_specification_name (str): The name of the target specification.
|
|
162
232
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
233
|
+
if not self.run or not self.params:
|
|
234
|
+
raise ValueError('Run instance or parameters not properly initialized')
|
|
235
|
+
|
|
236
|
+
# Type assertion to help the linter
|
|
237
|
+
assert isinstance(self.run, ToTaskRun)
|
|
238
|
+
assert isinstance(self.run.client, BackendClient)
|
|
239
|
+
|
|
240
|
+
client: BackendClient = self.run.client
|
|
167
241
|
task_params = {
|
|
168
242
|
'fields': 'id,data,data_unit',
|
|
169
243
|
'expand': 'data_unit',
|
|
170
244
|
}
|
|
171
|
-
for task_id in task_ids:
|
|
172
|
-
task = client.get_task(task_id, params=task_params)
|
|
173
|
-
data_file = task['data_unit']['files'].get(target_task_data_specification_code)
|
|
174
|
-
if not data_file:
|
|
175
|
-
self.run.log_message(f'File specification not found for task {task_id}')
|
|
176
|
-
self.run.log_annotate_task_data(
|
|
177
|
-
{'task_id': task_id, 'error': 'File specification not found'}, AnnotateTaskDataStatus.FAILED
|
|
178
|
-
)
|
|
179
|
-
continue
|
|
180
|
-
url = data_file.get('url')
|
|
181
|
-
if not url:
|
|
182
|
-
self.run.log_message(f'URL not found for task {task_id}')
|
|
183
|
-
self.run.log_annotate_task_data(
|
|
184
|
-
{'task_id': task_id, 'error': 'URL not found'}, AnnotateTaskDataStatus.FAILED
|
|
185
|
-
)
|
|
186
|
-
continue
|
|
187
245
|
|
|
246
|
+
total_tasks = len(task_ids)
|
|
247
|
+
success_count = 0
|
|
248
|
+
failed_count = 0
|
|
249
|
+
current_progress = 0
|
|
250
|
+
|
|
251
|
+
# Initialize metrics and progress
|
|
252
|
+
self._update_metrics(total_tasks, success_count, failed_count)
|
|
253
|
+
self.run.set_progress(0, total_tasks, category='annotate_task_data')
|
|
254
|
+
self.run.log_message('Annotating data to tasks...')
|
|
255
|
+
|
|
256
|
+
# Process each task
|
|
257
|
+
for task_id in task_ids:
|
|
188
258
|
try:
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
259
|
+
result = self._process_single_task(client, task_id, task_params, target_specification_name)
|
|
260
|
+
if result['success']:
|
|
261
|
+
success_count += 1
|
|
262
|
+
else:
|
|
263
|
+
failed_count += 1
|
|
264
|
+
|
|
265
|
+
current_progress += 1
|
|
266
|
+
self._update_metrics(total_tasks, success_count, failed_count)
|
|
267
|
+
self.run.set_progress(current_progress, total_tasks, category='annotate_task_data')
|
|
193
268
|
|
|
194
|
-
# Log success
|
|
195
|
-
self.run.log_annotate_task_data({'task_id': task_id, 'url': url}, AnnotateTaskDataStatus.SUCCESS)
|
|
196
269
|
except Exception as e:
|
|
197
|
-
self.run.log_message(f'Failed to
|
|
198
|
-
self.run.log_annotate_task_data(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
270
|
+
self.run.log_message(f'Failed to process task {task_id}: {str(e)}')
|
|
271
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': str(e)}, AnnotateTaskDataStatus.FAILED)
|
|
272
|
+
failed_count += 1
|
|
273
|
+
current_progress += 1
|
|
274
|
+
self._update_metrics(total_tasks, success_count, failed_count)
|
|
275
|
+
self.run.set_progress(current_progress, total_tasks, category='annotate_task_data')
|
|
276
|
+
|
|
277
|
+
# Finalize progress
|
|
278
|
+
self.run.set_progress(total_tasks, total_tasks, category='annotate_task_data')
|
|
279
|
+
self.run.log_message(f'Annotation completed. Success: {success_count}, Failed: {failed_count}')
|
|
280
|
+
|
|
281
|
+
def _process_single_task(
|
|
282
|
+
self, client: BackendClient, task_id: int, task_params: Dict[str, Any], target_specification_name: str
|
|
283
|
+
) -> Dict[str, Any]:
|
|
284
|
+
"""Process a single task for annotation.
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
client (BackendClient): The backend client instance.
|
|
288
|
+
task_id (int): The task ID to process.
|
|
289
|
+
task_params (Dict[str, Any]): Parameters for getting task data.
|
|
290
|
+
target_specification_name (str): The name of the target specification.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
Dict[str, Any]: Result dictionary with 'success' boolean and optional 'error' message.
|
|
294
|
+
"""
|
|
295
|
+
if not self.run:
|
|
296
|
+
raise ValueError('Run instance not properly initialized')
|
|
297
|
+
|
|
298
|
+
# Type assertion to help the linter
|
|
299
|
+
assert isinstance(self.run, ToTaskRun)
|
|
300
|
+
|
|
301
|
+
# Get task data
|
|
302
|
+
task_response = client.get_task(task_id, params=task_params)
|
|
303
|
+
if isinstance(task_response, str):
|
|
304
|
+
error_msg = 'Invalid task response'
|
|
305
|
+
self.run.log_message(f'{error_msg} received for task {task_id}')
|
|
306
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
307
|
+
return {'success': False, 'error': error_msg}
|
|
308
|
+
|
|
309
|
+
task: Dict[str, Any] = task_response
|
|
310
|
+
|
|
311
|
+
# Extract data file information
|
|
312
|
+
data_unit = task.get('data_unit', {})
|
|
313
|
+
files = data_unit.get('files', {})
|
|
314
|
+
data_file = files.get(target_specification_name)
|
|
315
|
+
|
|
316
|
+
if not data_file:
|
|
317
|
+
error_msg = 'File specification not found'
|
|
318
|
+
self.run.log_message(f'{error_msg} for task {task_id}')
|
|
319
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
320
|
+
return {'success': False, 'error': error_msg}
|
|
321
|
+
|
|
322
|
+
url = data_file.get('url')
|
|
323
|
+
if not url:
|
|
324
|
+
error_msg = 'URL not found'
|
|
325
|
+
self.run.log_message(f'{error_msg} for task {task_id}')
|
|
326
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
327
|
+
return {'success': False, 'error': error_msg}
|
|
328
|
+
|
|
329
|
+
# Fetch and process the data
|
|
330
|
+
try:
|
|
331
|
+
response = requests.get(url)
|
|
332
|
+
response.raise_for_status()
|
|
333
|
+
data = json.loads(response.content)
|
|
334
|
+
|
|
335
|
+
# Submit annotation data
|
|
336
|
+
client.annotate_task_data(task_id, data={'action': 'submit', 'data': data})
|
|
337
|
+
|
|
338
|
+
# Log success
|
|
339
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'url': url}, AnnotateTaskDataStatus.SUCCESS)
|
|
340
|
+
return {'success': True}
|
|
341
|
+
|
|
342
|
+
except requests.RequestException as e:
|
|
343
|
+
error_msg = f'Failed to fetch data from URL: {str(e)}'
|
|
344
|
+
self.run.log_message(f'{error_msg} for task {task_id}')
|
|
345
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
346
|
+
return {'success': False, 'error': error_msg}
|
|
347
|
+
except json.JSONDecodeError as e:
|
|
348
|
+
error_msg = f'Failed to parse JSON data: {str(e)}'
|
|
349
|
+
self.run.log_message(f'{error_msg} for task {task_id}')
|
|
350
|
+
self.run.log_annotate_task_data({'task_id': task_id, 'error': error_msg}, AnnotateTaskDataStatus.FAILED)
|
|
351
|
+
return {'success': False, 'error': error_msg}
|
|
352
|
+
|
|
353
|
+
def _update_metrics(self, total_tasks: int, success_count: int, failed_count: int):
|
|
354
|
+
"""Update metrics for task annotation progress.
|
|
202
355
|
|
|
203
|
-
|
|
356
|
+
Args:
|
|
357
|
+
total_tasks (int): Total number of tasks to process.
|
|
358
|
+
success_count (int): Number of successfully processed tasks.
|
|
359
|
+
failed_count (int): Number of failed tasks.
|
|
360
|
+
"""
|
|
361
|
+
if not self.run:
|
|
362
|
+
raise ValueError('Run instance not properly initialized')
|
|
363
|
+
|
|
364
|
+
# Type assertion to help the linter
|
|
365
|
+
assert isinstance(self.run, ToTaskRun)
|
|
366
|
+
|
|
367
|
+
metrics = self.run.MetricsRecord(
|
|
368
|
+
stand_by=total_tasks - success_count - failed_count, failed=failed_count, success=success_count
|
|
369
|
+
)
|
|
370
|
+
self.run.log_metrics(metrics, 'annotate_task_data')
|
|
371
|
+
|
|
372
|
+
def _handle_annotate_data_with_inference(self, task_ids: List[int]):
|
|
204
373
|
"""Handle annotate data with inference to tasks.
|
|
205
374
|
|
|
206
375
|
Args:
|
|
207
|
-
task_ids (
|
|
376
|
+
task_ids (List[int]): List of task IDs to annotate data to.
|
|
208
377
|
"""
|
|
378
|
+
if not self.run:
|
|
379
|
+
raise ValueError('Run instance not properly initialized')
|
|
380
|
+
|
|
209
381
|
self.run.log_message('Pre annotation with inference is not supported.')
|
|
210
382
|
self.run.end_log()
|
|
@@ -3,5 +3,17 @@ actions:
|
|
|
3
3
|
entrypoint: plugin.pre_annotation.pre_annotate
|
|
4
4
|
to_task:
|
|
5
5
|
entrypoint: plugin.to_task.AnnotationToTask
|
|
6
|
-
ui_schema:
|
|
7
|
-
|
|
6
|
+
ui_schema:
|
|
7
|
+
- $formkit: "radio"
|
|
8
|
+
name: "schema_to_convert"
|
|
9
|
+
label: "변환 스키마"
|
|
10
|
+
help: "변환 스키마 선택"
|
|
11
|
+
required: True
|
|
12
|
+
value: "<yolo>"
|
|
13
|
+
option:
|
|
14
|
+
- label: "Yolo"
|
|
15
|
+
value: "yolo"
|
|
16
|
+
- label: "Coco"
|
|
17
|
+
value: "coco"
|
|
18
|
+
- label: "Pascal"
|
|
19
|
+
value: "pascal"
|