thestage 0.6.4__py3-none-any.whl → 0.6.6__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.
- thestage/__init__.py +1 -1
- thestage/color_scheme/color_scheme.py +1 -0
- thestage/controllers/base_controller.py +4 -3
- thestage/controllers/config_controller.py +16 -4
- thestage/controllers/container_controller.py +147 -49
- thestage/controllers/instance_controller.py +35 -9
- thestage/controllers/project_controller.py +334 -86
- thestage/entities/container.py +5 -3
- thestage/entities/project_inference_simulator.py +2 -1
- thestage/entities/project_inference_simulator_model.py +1 -1
- thestage/entities/project_task.py +2 -3
- thestage/entities/rented_instance.py +2 -2
- thestage/entities/self_hosted_instance.py +2 -2
- thestage/helpers/error_handler.py +3 -3
- thestage/services/clients/git/git_client.py +8 -12
- thestage/services/clients/thestage_api/api_client.py +144 -109
- thestage/services/clients/thestage_api/dtos/base_controller/connect_resolve_response.py +21 -0
- thestage/services/clients/thestage_api/dtos/container_param_request.py +1 -1
- thestage/services/clients/thestage_api/dtos/container_response.py +1 -21
- thestage/services/clients/thestage_api/dtos/docker_container_controller/docker_container_list_request.py +2 -1
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_request.py +7 -1
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_instance_response.py +0 -1
- thestage/services/clients/thestage_api/dtos/inference_controller/deploy_inference_model_to_sagemaker_request.py +1 -0
- thestage/services/clients/thestage_api/dtos/inference_controller/get_inference_simulator_request.py +2 -1
- thestage/services/clients/thestage_api/dtos/inference_controller/{inference_simulator_list_for_project_request.py → inference_simulator_list_request.py} +3 -2
- thestage/services/clients/thestage_api/dtos/inference_controller/{inference_simulator_list_for_project_response.py → inference_simulator_list_response.py} +1 -1
- thestage/services/clients/thestage_api/dtos/inference_controller/inference_simulator_model_list_for_project_request.py +2 -1
- thestage/services/clients/thestage_api/dtos/instance_rented_response.py +4 -37
- thestage/services/clients/thestage_api/dtos/logging_controller/log_polling_request.py +3 -3
- thestage/services/clients/thestage_api/dtos/logging_controller/user_logs_query_request.py +3 -11
- thestage/services/clients/thestage_api/dtos/project_controller/project_get_deploy_ssh_key_request.py +1 -0
- thestage/services/clients/thestage_api/dtos/project_controller/project_push_inference_simulator_model_request.py +2 -1
- thestage/services/clients/thestage_api/dtos/project_controller/project_run_task_request.py +2 -4
- thestage/services/clients/thestage_api/dtos/project_controller/project_start_inference_simulator_request.py +5 -3
- thestage/services/clients/thestage_api/dtos/project_response.py +3 -15
- thestage/services/clients/thestage_api/dtos/selfhosted_instance_response.py +2 -20
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_key_to_user_response.py +1 -1
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/add_ssh_public_key_to_instance_request.py +4 -2
- thestage/services/clients/thestage_api/dtos/ssh_key_controller/is_user_has_public_ssh_key_response.py +1 -1
- thestage/services/clients/thestage_api/dtos/task_controller/task_list_for_project_request.py +4 -1
- thestage/services/clients/thestage_api/dtos/task_controller/task_view_response.py +0 -2
- thestage/services/config_provider/config_provider.py +2 -2
- thestage/services/connect/connect_service.py +76 -73
- thestage/services/container/container_service.py +23 -19
- thestage/services/container/mapper/container_mapper.py +2 -1
- thestage/services/instance/instance_service.py +13 -20
- thestage/services/instance/mapper/instance_mapper.py +1 -3
- thestage/services/instance/mapper/selfhosted_mapper.py +3 -4
- thestage/services/logging/logging_service.py +40 -40
- thestage/services/project/dto/inference_simulator_dto.py +1 -10
- thestage/services/project/dto/inference_simulator_model_dto.py +2 -10
- thestage/services/project/dto/project_config.py +3 -2
- thestage/services/project/mapper/project_inference_simulator_mapper.py +1 -0
- thestage/services/project/mapper/project_inference_simulator_model_mapper.py +3 -3
- thestage/services/project/mapper/project_task_mapper.py +2 -3
- thestage/services/project/project_service.py +161 -131
- thestage/services/remote_server_service.py +1 -0
- thestage/services/task/dto/task_dto.py +3 -23
- {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/METADATA +3 -2
- {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/RECORD +63 -72
- {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/WHEEL +1 -1
- thestage/services/clients/thestage_api/dtos/cloud_provider_region.py +0 -19
- thestage/services/clients/thestage_api/dtos/docker_container_assigned_device.py +0 -10
- thestage/services/clients/thestage_api/dtos/enums/currency_type.py +0 -10
- thestage/services/clients/thestage_api/dtos/enums/daemon_status.py +0 -9
- thestage/services/clients/thestage_api/dtos/enums/disk_type.py +0 -7
- thestage/services/clients/thestage_api/dtos/enums/drive_type.py +0 -7
- thestage/services/clients/thestage_api/dtos/enums/instance_type.py +0 -7
- thestage/services/clients/thestage_api/dtos/enums/location_region.py +0 -11
- thestage/services/clients/thestage_api/dtos/enums/power_status.py +0 -10
- thestage/services/clients/thestage_api/dtos/price_definition.py +0 -14
- {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info}/entry_points.txt +0 -0
- {thestage-0.6.4.dist-info → thestage-0.6.6.dist-info/licenses}/LICENSE.txt +0 -0
|
@@ -43,9 +43,9 @@ class LoggingService:
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
@error_handler()
|
|
46
|
-
def print_last_task_logs(self,
|
|
46
|
+
def print_last_task_logs(self, task_public_id: str, logs_number: Optional[int]):
|
|
47
47
|
logs = self.__thestage_api_client.query_user_logs(
|
|
48
|
-
|
|
48
|
+
task_public_id=task_public_id,
|
|
49
49
|
limit=logs_number
|
|
50
50
|
)
|
|
51
51
|
for log_message in reversed(logs.queryResult):
|
|
@@ -53,9 +53,9 @@ class LoggingService:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
@error_handler()
|
|
56
|
-
def print_last_inference_simulator_logs(self,
|
|
56
|
+
def print_last_inference_simulator_logs(self, inference_simulator_public_id: str, logs_number: Optional[int]):
|
|
57
57
|
logs = self.__thestage_api_client.query_user_logs(
|
|
58
|
-
|
|
58
|
+
inference_simulator_public_id=inference_simulator_public_id,
|
|
59
59
|
limit=logs_number
|
|
60
60
|
)
|
|
61
61
|
for log_message in reversed(logs.queryResult):
|
|
@@ -63,9 +63,10 @@ class LoggingService:
|
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
@error_handler()
|
|
66
|
-
def print_last_container_logs(self,
|
|
66
|
+
def print_last_container_logs(self, container_public_id: Optional[str], container_slug: Optional[str], logs_number: Optional[int]):
|
|
67
67
|
container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
|
|
68
|
-
|
|
68
|
+
container_public_id=container_public_id,
|
|
69
|
+
container_slug=container_slug,
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
if not container:
|
|
@@ -73,7 +74,7 @@ class LoggingService:
|
|
|
73
74
|
raise typer.Exit(1)
|
|
74
75
|
|
|
75
76
|
logs = self.__thestage_api_client.query_user_logs(
|
|
76
|
-
|
|
77
|
+
container_public_id=container.public_id,
|
|
77
78
|
limit=logs_number
|
|
78
79
|
)
|
|
79
80
|
for log_message in reversed(logs.queryResult):
|
|
@@ -81,15 +82,15 @@ class LoggingService:
|
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
@error_handler()
|
|
84
|
-
def stream_task_logs_with_controls(self,
|
|
85
|
+
def stream_task_logs_with_controls(self, task_public_id: str):
|
|
85
86
|
asyncio.run(
|
|
86
|
-
self.__stream_task_logs_with_controls_async(
|
|
87
|
+
self.__stream_task_logs_with_controls_async(task_public_id=task_public_id)
|
|
87
88
|
)
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
@error_handler()
|
|
91
|
-
async def __stream_task_logs_with_controls_async(self,
|
|
92
|
-
task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(
|
|
92
|
+
async def __stream_task_logs_with_controls_async(self, task_public_id: str):
|
|
93
|
+
task_view_response: Optional[TaskViewResponse] = self.__thestage_api_client.get_task(task_public_id=task_public_id,)
|
|
93
94
|
|
|
94
95
|
task_status_map: Dict[str, str] = self.__thestage_api_client.get_task_localized_status_map()
|
|
95
96
|
|
|
@@ -98,25 +99,20 @@ class LoggingService:
|
|
|
98
99
|
if task:
|
|
99
100
|
if task.frontend_status.status_key not in [TaskStatus.RUNNING, TaskStatus.SCHEDULED]:
|
|
100
101
|
typer.echo(__("Task must be in status '%required_status%' to stream real-time logs. Task %task_id% status: '%status%'.", {
|
|
101
|
-
'task_id': str(task.
|
|
102
|
+
'task_id': str(task.public_id),
|
|
102
103
|
'status': task.frontend_status.status_translation,
|
|
103
104
|
'required_status': task_status_map.get(TaskStatus.RUNNING) or TaskStatus.RUNNING
|
|
104
105
|
}))
|
|
105
106
|
raise typer.Exit(1)
|
|
106
107
|
else:
|
|
107
|
-
typer.echo(
|
|
108
|
+
typer.echo(f"Task with ID {task_public_id} was not found")
|
|
108
109
|
raise typer.Exit(1)
|
|
109
110
|
|
|
110
|
-
typer.echo(
|
|
111
|
-
f"Log stream for task %task_id% started",
|
|
112
|
-
{
|
|
113
|
-
'task_id': str(task.id),
|
|
114
|
-
}
|
|
115
|
-
))
|
|
111
|
+
typer.echo(f"Log stream for task {task.public_id} started")
|
|
116
112
|
|
|
117
113
|
typer.echo(__("CTRL+C to cancel the task. CTRL+D to disconnect from log stream."))
|
|
118
114
|
|
|
119
|
-
print_logs_task = asyncio.create_task(self.print_realtime_logs(
|
|
115
|
+
print_logs_task = asyncio.create_task(self.print_realtime_logs(task_public_id=task.public_id))
|
|
120
116
|
input_task = asyncio.create_task(self.read_log_stream_input())
|
|
121
117
|
|
|
122
118
|
def sigint_handler():
|
|
@@ -131,22 +127,24 @@ class LoggingService:
|
|
|
131
127
|
if input_task in done:
|
|
132
128
|
print_logs_task.cancel()
|
|
133
129
|
if not input_task.result(): # result is only expected if ctrl+D triggered EOFError
|
|
134
|
-
typer.echo(f"\rTask {
|
|
130
|
+
typer.echo(f"\rTask {task_public_id} will be canceled")
|
|
135
131
|
self.__thestage_api_client.cancel_task(
|
|
136
|
-
|
|
132
|
+
task_public_id=task.public_id,
|
|
137
133
|
)
|
|
138
134
|
|
|
139
135
|
@error_handler()
|
|
140
|
-
def stream_inference_simulator_logs_with_controls(self, slug: str):
|
|
136
|
+
def stream_inference_simulator_logs_with_controls(self, public_id: Optional[str] = None, slug: Optional[str] = None):
|
|
141
137
|
asyncio.run(
|
|
142
138
|
self.__stream_inference_simulator_logs_with_controls_async(
|
|
139
|
+
public_id=public_id,
|
|
143
140
|
slug=slug
|
|
144
141
|
)
|
|
145
142
|
)
|
|
146
143
|
|
|
147
144
|
@error_handler()
|
|
148
|
-
async def __stream_inference_simulator_logs_with_controls_async(self, slug: str):
|
|
145
|
+
async def __stream_inference_simulator_logs_with_controls_async(self, public_id: Optional[str], slug: Optional[str]):
|
|
149
146
|
get_inference_simulator_response: Optional[GetInferenceSimulatorResponse] = self.__thestage_api_client.get_inference_simulator(
|
|
147
|
+
public_id=public_id,
|
|
150
148
|
slug=slug,
|
|
151
149
|
)
|
|
152
150
|
|
|
@@ -177,7 +175,7 @@ class LoggingService:
|
|
|
177
175
|
typer.echo(__("CTRL+D to disconnect from log stream."))
|
|
178
176
|
|
|
179
177
|
print_task_or_inference_simulator_logs = asyncio.create_task(
|
|
180
|
-
self.print_realtime_logs(
|
|
178
|
+
self.print_realtime_logs(inference_simulator_public_id=inference_simulator.public_id)
|
|
181
179
|
)
|
|
182
180
|
input_task = asyncio.create_task(self.read_log_stream_input())
|
|
183
181
|
|
|
@@ -190,18 +188,20 @@ class LoggingService:
|
|
|
190
188
|
|
|
191
189
|
|
|
192
190
|
@error_handler()
|
|
193
|
-
def stream_container_logs_with_controls(self,
|
|
191
|
+
def stream_container_logs_with_controls(self, container_public_id: Optional[str], container_slug: Optional[str]):
|
|
194
192
|
asyncio.run(
|
|
195
193
|
self.__stream_container_logs_with_controls_async(
|
|
196
|
-
|
|
194
|
+
container_public_id=container_public_id,
|
|
195
|
+
container_slug=container_slug
|
|
197
196
|
)
|
|
198
197
|
)
|
|
199
198
|
|
|
200
199
|
|
|
201
200
|
@error_handler()
|
|
202
|
-
async def __stream_container_logs_with_controls_async(self,
|
|
201
|
+
async def __stream_container_logs_with_controls_async(self, container_public_id: Optional[str], container_slug: Optional[str]):
|
|
203
202
|
container: Optional[DockerContainerDto] = self.__thestage_api_client.get_container(
|
|
204
|
-
|
|
203
|
+
container_public_id=container_public_id,
|
|
204
|
+
container_slug=container_slug,
|
|
205
205
|
)
|
|
206
206
|
|
|
207
207
|
if container:
|
|
@@ -214,7 +214,7 @@ class LoggingService:
|
|
|
214
214
|
typer.echo(f"Log stream for Docker container started")
|
|
215
215
|
typer.echo("CTRL+D to disconnect from log stream.")
|
|
216
216
|
|
|
217
|
-
print_logs_task = asyncio.create_task(self.print_realtime_logs(
|
|
217
|
+
print_logs_task = asyncio.create_task(self.print_realtime_logs(docker_container_public_id=container.public_id))
|
|
218
218
|
input_task = asyncio.create_task(self.read_log_stream_input())
|
|
219
219
|
|
|
220
220
|
def sigint_handler():
|
|
@@ -243,9 +243,9 @@ class LoggingService:
|
|
|
243
243
|
|
|
244
244
|
async def print_realtime_logs(
|
|
245
245
|
self,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
task_public_id: Optional[str] = None,
|
|
247
|
+
inference_simulator_public_id: Optional[str] = None,
|
|
248
|
+
docker_container_public_id: Optional[str] = None,
|
|
249
249
|
):
|
|
250
250
|
polling_interval_seconds: float = 4 # also adjust polling api method timeout if changed
|
|
251
251
|
between_logs_sleeping_coef: float = 1 # we emulate delay between logs, but if for any reason code runs for too long - delays will be controlled with this coef
|
|
@@ -269,9 +269,9 @@ class LoggingService:
|
|
|
269
269
|
# print_nonblocking(f'TDIFF {stream_to_logs_diff.total_seconds()}', writer)
|
|
270
270
|
try:
|
|
271
271
|
logs_response = await self.__thestage_api_client.poll_logs_httpx(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
272
|
+
task_public_id=task_public_id,
|
|
273
|
+
inference_simulator_public_id=inference_simulator_public_id,
|
|
274
|
+
docker_container_public_id=docker_container_public_id,
|
|
275
275
|
last_log_timestamp=last_iteration_log_timestamp,
|
|
276
276
|
last_log_id=last_log_id
|
|
277
277
|
)
|
|
@@ -314,11 +314,11 @@ class LoggingService:
|
|
|
314
314
|
|
|
315
315
|
if consecutive_error_count > 7:
|
|
316
316
|
seconds_with_error = (datetime.utcnow() - errors_started_at).total_seconds()
|
|
317
|
-
if
|
|
317
|
+
if inference_simulator_public_id:
|
|
318
318
|
print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage inference-simulator logs <inference-simulator-UID>' to reconnect.", writer)
|
|
319
|
-
elif
|
|
320
|
-
print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage project task logs {
|
|
321
|
-
elif
|
|
319
|
+
elif task_public_id:
|
|
320
|
+
print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage project task logs {task_public_id}' to reconnect.", writer)
|
|
321
|
+
elif docker_container_public_id:
|
|
322
322
|
print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds). Try 'thestage container logs <docker-container-UID>' to reconnect.", writer)
|
|
323
323
|
else:
|
|
324
324
|
print_nonblocking(f"Log stream: disconnected from server (connectivity issues for {seconds_with_error} seconds)", writer)
|
|
@@ -4,20 +4,11 @@ from pydantic import BaseModel, ConfigDict, Field
|
|
|
4
4
|
class InferenceSimulatorDto(BaseModel):
|
|
5
5
|
model_config = ConfigDict(use_enum_values=True)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
client_id: Optional[int] = Field(None, alias="clientId")
|
|
9
|
-
title: Optional[str] = Field(None, alias="title")
|
|
7
|
+
public_id: Optional[str] = Field(None, alias="publicId")
|
|
10
8
|
slug: Optional[str] = Field(None, alias="slug")
|
|
11
|
-
docker_container_id: Optional[int] = Field(None, alias="dockerContainerId")
|
|
12
|
-
instance_rented_id: Optional[int] = Field(None, alias="instanceRentedId")
|
|
13
|
-
selfhosted_instance_id: Optional[int] = Field(None, alias="selfhostedInstanceId")
|
|
14
|
-
project_id: Optional[int] = Field(None, alias="projectId")
|
|
15
9
|
status: Optional[str] = Field(None, alias="status")
|
|
16
|
-
commit_hash: Optional[str] = Field(None, alias="commitHash")
|
|
17
10
|
http_endpoint: Optional[str] = Field(None, alias="httpEndpoint")
|
|
18
11
|
grpc_endpoint: Optional[str] = Field(None, alias="grpcEndpoint")
|
|
19
12
|
metrics_endpoint: Optional[str] = Field(None, alias="metricsEndpoint")
|
|
20
|
-
inference_metadata: Optional[Dict[str, Any]] = Field(None, alias="inferenceMetadata")
|
|
21
13
|
qlip_serve_metadata: Optional[Dict[str, Any]] = Field(None, alias="qlipServeMetadata")
|
|
22
14
|
created_at: Optional[str] = Field(None, alias="createdAt")
|
|
23
|
-
updated_at: Optional[str] = Field(None, alias="updatedAt")
|
|
@@ -4,18 +4,10 @@ from pydantic import BaseModel, ConfigDict, Field
|
|
|
4
4
|
class InferenceSimulatorModelDto(BaseModel):
|
|
5
5
|
model_config = ConfigDict(use_enum_values=True)
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
public_id: Optional[str] = Field(None, alias='publicId')
|
|
8
8
|
slug: Optional[str] = Field(None, alias='slug')
|
|
9
|
-
client_id: Optional[int] = Field(None, alias='clientId')
|
|
10
|
-
instance_rented_id: Optional[int] = Field(None, alias='instanceRentedId')
|
|
11
|
-
selfhosted_instance_id: Optional[int] = Field(None, alias='selfhostedInstanceId')
|
|
12
|
-
project_id: Optional[int] = Field(None, alias='projectId')
|
|
13
9
|
status: Optional[str] = Field(None, alias='status')
|
|
14
10
|
environment_metadata: Optional[Dict[str, Any]] = Field(None, alias='environmentMetadata')
|
|
15
11
|
commit_hash: Optional[str] = Field(None, alias='commitHash')
|
|
16
|
-
cpu_architecture: Optional[str] = Field(None, alias='cpuArchitecture')
|
|
17
|
-
gpu_model: Optional[str] = Field(None, alias='gpuModel')
|
|
18
|
-
ecr_image_url: Optional[str] = Field(None, alias='ecrImageUrl')
|
|
19
|
-
s3_artifacts_url: Optional[str] = Field(None, alias='s3ArtifactsUrl')
|
|
20
12
|
created_at: Optional[str] = Field(None, alias='createdAt')
|
|
21
|
-
updated_at: Optional[str] = Field(None, alias='updatedAt')
|
|
13
|
+
updated_at: Optional[str] = Field(None, alias='updatedAt')
|
|
@@ -6,9 +6,10 @@ from pydantic import Field, ConfigDict, BaseModel
|
|
|
6
6
|
class ProjectConfig(BaseModel):
|
|
7
7
|
model_config = ConfigDict(use_enum_values=True)
|
|
8
8
|
|
|
9
|
-
id: int = Field(None, alias='id')
|
|
10
9
|
slug: str = Field(None, alias='slug')
|
|
10
|
+
public_id: str = Field(None, alias='public_id')
|
|
11
11
|
git_repository_url: str = Field(None, alias='git_repository_url')
|
|
12
12
|
deploy_key_path: str = Field(None, alias='deploy_key_path')
|
|
13
|
-
default_container_uid: Optional[str] = Field(None, alias='default_container_uid')
|
|
13
|
+
default_container_uid: Optional[str] = Field(None, alias='default_container_uid') # TODO delete
|
|
14
|
+
default_container_public_id: Optional[str] = Field(None, alias='default_container_public_id') # TODO use
|
|
14
15
|
prompt_for_default_container: bool = Field(True, alias='prompt_for_default_container')
|
|
@@ -11,11 +11,11 @@ class ProjectInferenceSimulatorModelMapper(AbstractMapper):
|
|
|
11
11
|
return None
|
|
12
12
|
|
|
13
13
|
return ProjectInferenceSimulatorModelEntity(
|
|
14
|
-
|
|
14
|
+
public_id=item.public_id,
|
|
15
15
|
slug=item.slug,
|
|
16
16
|
status=item.status or '',
|
|
17
17
|
commit_hash=item.commit_hash or '',
|
|
18
|
-
environment_metadata=item.environment_metadata or
|
|
18
|
+
environment_metadata=item.environment_metadata or {},
|
|
19
19
|
started_at=item.created_at or '',
|
|
20
|
-
finished_at=item.updated_at or ''
|
|
20
|
+
finished_at=item.updated_at or '' # TODO updated_at cannot be finished_at
|
|
21
21
|
)
|
|
@@ -12,11 +12,10 @@ class ProjectTaskMapper(AbstractMapper):
|
|
|
12
12
|
return None
|
|
13
13
|
|
|
14
14
|
return ProjectTaskEntity(
|
|
15
|
-
|
|
15
|
+
public_id=item.public_id or '',
|
|
16
16
|
title=item.title or '',
|
|
17
17
|
status=item.frontend_status.status_translation or '',
|
|
18
|
-
|
|
19
|
-
docker_container_slug=(str(item.docker_container.slug) or '') if item.docker_container else '',
|
|
18
|
+
docker_container_public_id=item.docker_container_public_id,
|
|
20
19
|
started_at=item.started_at or '',
|
|
21
20
|
finished_at=item.finished_at or '',
|
|
22
21
|
)
|