thestage 0.6.8__py3-none-any.whl → 0.7.0__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  from . import *
2
2
  __app_name__ = "thestage"
3
- __version__ = "0.6.8"
3
+ __version__ = "0.7.0"
thestage/cli_command.py CHANGED
@@ -27,10 +27,13 @@ class CliCommand(str, Enum):
27
27
  CONTAINER_CONNECT = "CONTAINER_CONNECT"
28
28
  CONTAINER_UPLOAD = "CONTAINER_UPLOAD"
29
29
  CONTAINER_DOWNLOAD = "CONTAINER_DOWNLOAD"
30
+ CONTAINER_CREATE = "CONTAINER_CREATE"
30
31
  CONTAINER_START = "CONTAINER_START"
31
32
  CONTAINER_STOP = "CONTAINER_STOP"
33
+ CONTAINER_DELETE = "CONTAINER_DELETE"
32
34
  CONTAINER_RESTART = "CONTAINER_RESTART"
33
35
  CONTAINER_LOGS = "CONTAINER_LOGS"
36
+ CONTAINER_IMAGE_LS = "CONTAINER_IMAGE_LS"
34
37
  INSTANCE_RENTED_LS = "INSTANCE_RENTED_LS"
35
38
  INSTANCE_RENTED_CONNECT = "INSTANCE_RENTED_CONNECT"
36
39
  INSTANCE_SELF_HOSTED_LS = "INSTANCE_SELF_HOSTED_LS"
@@ -7,19 +7,24 @@ import typer
7
7
 
8
8
  from thestage.color_scheme.color_scheme import ColorScheme
9
9
  from thestage.connect.communication.connect_api_client import ConnectApiClient
10
+ from thestage.docker_container.business.mapper.image_mapper import ImageMapper
10
11
  from thestage.docker_container.communication.docker_container_api_client import DockerContainerApiClient
11
12
  from thestage.docker_container.dto.container_entity import DockerContainerEntity
12
13
  from thestage.docker_container.dto.container_action_request import DockerContainerActionRequest
14
+ from thestage.docker_container.dto.docker_container_create_request import DockerContainerCreateRequest, \
15
+ DockerContainerMappings
13
16
  from thestage.docker_container.dto.enum.container_pending_action import DockerContainerAction
14
17
  from thestage.docker_container.dto.enum.container_status import DockerContainerStatus
18
+ from thestage.docker_container.dto.image_entity import DockerImageEntity
15
19
  from thestage.global_dto.enums.shell_type import ShellType
20
+ from thestage.project.dto.project_config import ProjectConfig
16
21
  from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
17
22
  from thestage.docker_container.business.mapper.container_mapper import ContainerMapper
18
23
  from thestage.services.filesystem_service import FileSystemService
19
24
  from thestage.connect.business.remote_server_service import RemoteServerService
20
25
  from thestage.i18n.translation import __
21
26
  from thestage.services.abstract_service import AbstractService
22
- from thestage.docker_container.dto.container_response import DockerContainerDto
27
+ from thestage.docker_container.dto.container_response import DockerContainerDto, DockerImageDto
23
28
  from thestage.helpers.error_handler import error_handler
24
29
  from thestage.config.business.config_provider import ConfigProvider
25
30
 
@@ -118,6 +123,7 @@ class ContainerService(AbstractService):
118
123
 
119
124
  return list
120
125
 
126
+
121
127
  # TODO delete this proxy method
122
128
  @error_handler()
123
129
  def get_container(
@@ -456,3 +462,117 @@ class ContainerService(AbstractService):
456
462
 
457
463
  if result.is_success:
458
464
  typer.echo(f'Docker container action scheduled: {action.value}')
465
+
466
+ @error_handler()
467
+ def create_docker_container(
468
+ self,
469
+ project_public_id: Optional[str],
470
+ project_slug: Optional[str],
471
+ container_slug: str,
472
+ rented_instance_public_id: Optional[str],
473
+ rented_instance_slug: Optional[str],
474
+ self_hosted_instance_public_id: Optional[str],
475
+ self_hosted_instance_slug: Optional[str],
476
+ image_name: str,
477
+ cpu_number: Optional[int],
478
+ gpu_number: Optional[int],
479
+ volume_mappings: Optional[Dict[str, str]],
480
+ ):
481
+ if not project_public_id and not project_slug:
482
+ project_config: ProjectConfig = self.__config_provider.read_project_config()
483
+ if not project_config:
484
+ typer.echo("Provide the project unique ID or name, or run this command from within an initialized project directory")
485
+ raise typer.Exit(1)
486
+ project_public_id = project_config.public_id
487
+
488
+ docker_images_paginated = self.get_image_list()
489
+ if not docker_images_paginated or not docker_images_paginated.entities:
490
+ typer.echo("No Docker images available")
491
+ raise typer.Exit(1)
492
+
493
+ selected_image = None
494
+ selection_pool = docker_images_paginated.entities
495
+
496
+ if image_name is not None:
497
+ matches = [img for img in selection_pool if img.image == image_name]
498
+
499
+ if len(matches) == 1:
500
+ selected_image = matches[0]
501
+ elif len(matches) > 1:
502
+ typer.echo(f"Multiple tags found for repository '{image_name}':")
503
+ selection_pool = matches
504
+ else:
505
+ typer.echo(f"Image '{image_name}' not found. Select from available images:")
506
+
507
+ if selected_image is None:
508
+ for idx, img in enumerate(selection_pool, start=1):
509
+ typer.echo(f"{idx}) {img.image}:{img.tag}")
510
+
511
+ choice_str = typer.prompt("Choose which image to use")
512
+
513
+ try:
514
+ choice_index = int(choice_str)
515
+ except ValueError:
516
+ typer.echo("Error: Invalid input. Enter a number.")
517
+ raise typer.Exit(1)
518
+
519
+ if not (1 <= choice_index <= len(selection_pool)):
520
+ typer.echo("Error: Choice out of range.")
521
+ raise typer.Exit(1)
522
+
523
+ selected_image = selection_pool[choice_index - 1]
524
+
525
+ request_params = DockerContainerCreateRequest(
526
+ instance_rented_public_id=rented_instance_public_id,
527
+ instance_rented_slug=rented_instance_slug,
528
+ selfhosted_instance_public_id=self_hosted_instance_public_id,
529
+ selfhosted_instance_slug=self_hosted_instance_slug,
530
+ project_public_id=project_public_id,
531
+ project_slug=project_slug,
532
+ container_slug=container_slug,
533
+ docker_image_id=selected_image.id,
534
+ assigned_cpus=cpu_number,
535
+ is_unlimited_cpus=cpu_number is None,
536
+ assigned_gpus=gpu_number,
537
+ is_unlimited_gpus=gpu_number is None,
538
+ mappings=DockerContainerMappings(directory_mappings=volume_mappings)
539
+ )
540
+
541
+ result = self.__docker_container_api_client.create_container(
542
+ request_params
543
+ )
544
+ if result.is_success:
545
+ typer.echo(f'Docker container creation scheduled.')
546
+
547
+ @error_handler()
548
+ def print_docker_image_list(
549
+ self,
550
+ row: int,
551
+ page: int,
552
+ ):
553
+ typer.echo("Listing available Docker images:")
554
+
555
+ self.print(
556
+ func_get_data=self.get_image_list,
557
+ func_special_params={},
558
+ mapper=ImageMapper(),
559
+ headers=list(map(lambda x: x.alias, DockerImageEntity.model_fields.values())),
560
+ row=row,
561
+ page=page,
562
+ max_col_width=[50, 20],
563
+ show_index="never",
564
+ )
565
+
566
+ @error_handler()
567
+ def get_image_list(
568
+ self,
569
+ row: int = 5,
570
+ page: int = 1,
571
+ ) -> PaginatedEntityList[DockerImageDto]:
572
+
573
+ list = self.__docker_container_api_client.get_docker_image_list(
574
+ page=page,
575
+ limit=row,
576
+ )
577
+
578
+ return list
@@ -0,0 +1,16 @@
1
+ from typing import Optional
2
+
3
+ from thestage.docker_container.dto.container_response import DockerImageDto
4
+ from thestage.docker_container.dto.image_entity import DockerImageEntity
5
+ from thestage.services.abstract_mapper import AbstractMapper
6
+
7
+
8
+ class ImageMapper(AbstractMapper):
9
+ def build_entity(self, item: DockerImageDto) -> Optional[DockerImageEntity]:
10
+ if not item:
11
+ return None
12
+
13
+ return DockerImageEntity(
14
+ image=item.image or '',
15
+ tag=item.tag or '',
16
+ )
@@ -15,7 +15,6 @@ from thestage.logging.business.logging_service import LoggingService
15
15
 
16
16
  app = typer.Typer(no_args_is_help=True, help=__("Manage containers"))
17
17
 
18
-
19
18
  @app.command(name='ls', help=__("List containers"), **get_command_metadata(CliCommand.CONTAINER_LS))
20
19
  def list_containers(
21
20
  row: int = typer.Option(
@@ -289,7 +288,7 @@ def start_container(
289
288
  check_command_permission(command_name)
290
289
 
291
290
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
292
- typer.echo("Please provide a single identifier for container - ID or name.")
291
+ typer.echo("Provide a single identifier for container - ID or name.")
293
292
  raise typer.Exit(1)
294
293
 
295
294
  service_factory = validate_config_and_get_service_factory()
@@ -301,7 +300,140 @@ def start_container(
301
300
  action=DockerContainerAction.START
302
301
  )
303
302
 
304
- app_logger.info(f'Container start completed')
303
+ app_logger.info(f'Container is starting')
304
+ raise typer.Exit(0)
305
+
306
+
307
+ @app.command(name="create", no_args_is_help=True, help=__("Create container"), **get_command_metadata(CliCommand.CONTAINER_CREATE))
308
+ def create_container(
309
+ project_public_id: Optional[str] = typer.Option(
310
+ None,
311
+ '--project-id',
312
+ '-pid',
313
+ help=__("Project ID. By default, project info is taken from the current directory"),
314
+ is_eager=False,
315
+ ),
316
+ project_slug: Optional[str] = typer.Option(
317
+ None,
318
+ '--project-name',
319
+ '-pn',
320
+ help=__("Project name. By default, project info is taken from the current directory"),
321
+ is_eager=False,
322
+ ),
323
+ working_directory: Optional[str] = typer.Option(
324
+ None,
325
+ "--working-directory",
326
+ "-wd",
327
+ help=__("Full path to working directory"),
328
+ show_default=False,
329
+ is_eager=False,
330
+ ),
331
+ container_slug: Optional[str] = typer.Option(
332
+ None,
333
+ '--container-name',
334
+ '-cn',
335
+ help=__("Container name"),
336
+ is_eager=False,
337
+ ),
338
+ rented_instance_public_id: Optional[str] = typer.Option(
339
+ None,
340
+ '--rented-instance-id',
341
+ '-rid',
342
+ help=__("Rented instance ID on which container will run"),
343
+ is_eager=False,
344
+ ),
345
+ rented_instance_slug: Optional[str] = typer.Option(
346
+ None,
347
+ '--rented-instance-name',
348
+ '-rn',
349
+ help=__("Rented instance name on which container will run"),
350
+ is_eager=False,
351
+ ),
352
+ self_hosted_instance_public_id: Optional[str] = typer.Option(
353
+ None,
354
+ '--self-hosted-instance-id',
355
+ '-sid',
356
+ help=__("Self-hosted instance ID on which container will run"),
357
+ is_eager=False,
358
+ ),
359
+ self_hosted_instance_slug: Optional[str] = typer.Option(
360
+ None,
361
+ '--self-hosted-instance-name',
362
+ '-sn',
363
+ help=__("Self-hosted instance name on which container will run"),
364
+ is_eager=False,
365
+ ),
366
+ image_name: Optional[str] = typer.Option(
367
+ None,
368
+ '--image',
369
+ '-i',
370
+ help=__("Docker image name. If not provided, you will be prompted to select from available images"),
371
+ is_eager=False,
372
+ ),
373
+ gpu_number: Optional[int] = typer.Option(
374
+ None,
375
+ '--gpu-number',
376
+ '-gpun',
377
+ help=__("Number of assigned GPUs for container. No GPU limit by default"),
378
+ is_eager=False,
379
+ ),
380
+ cpu_number: Optional[int] = typer.Option(
381
+ None,
382
+ '--cpu-number',
383
+ '-cpun',
384
+ help=__("Number of assigned CPUs for container. No CPU limit by default"),
385
+ is_eager=False,
386
+ ),
387
+ volume_mounts: Optional[List[str]] = typer.Option(
388
+ None,
389
+ '--volume',
390
+ '-v',
391
+ help=__("Volume mounts. Can be used multiple times. Format: /host_dir:/container_dir"),
392
+ is_eager=False,
393
+ ),
394
+ ):
395
+ command_name = CliCommand.CONTAINER_CREATE
396
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
397
+ check_command_permission(command_name)
398
+
399
+ if sum(v is not None for v in [project_public_id, project_slug]) > 1:
400
+ typer.echo("Provide a single identifier for project - ID or name.")
401
+ raise typer.Exit(1)
402
+
403
+ if sum(v is not None for v in [rented_instance_public_id, rented_instance_slug, self_hosted_instance_public_id, self_hosted_instance_slug]) != 1:
404
+ typer.echo("Provide exactly one identifier (ID or Name) for either a rented or self-hosted instance.")
405
+ raise typer.Exit(1)
406
+
407
+ if not container_slug:
408
+ container_slug = typer.prompt("Enter container name")
409
+
410
+ volume_mappings_dict = {}
411
+ if volume_mounts:
412
+ for mount in volume_mounts:
413
+ if ":" not in mount:
414
+ typer.echo(f"Invalid volume format: {mount}. Expected host:container")
415
+ raise typer.Exit(1)
416
+ host_path, container_path = mount.split(":", 1)
417
+ volume_mappings_dict[host_path] = container_path
418
+
419
+ service_factory = validate_config_and_get_service_factory(working_directory=working_directory)
420
+ container_service: ContainerService = service_factory.get_container_service()
421
+
422
+ container_service.create_docker_container(
423
+ project_public_id=project_public_id,
424
+ project_slug=project_slug,
425
+ container_slug=container_slug,
426
+ rented_instance_public_id=rented_instance_public_id,
427
+ rented_instance_slug=rented_instance_slug,
428
+ self_hosted_instance_public_id=self_hosted_instance_public_id,
429
+ self_hosted_instance_slug=self_hosted_instance_slug,
430
+ image_name=image_name,
431
+ cpu_number=cpu_number,
432
+ gpu_number=gpu_number,
433
+ volume_mappings=volume_mappings_dict
434
+ )
435
+
436
+ app_logger.info(f'Container is creating')
305
437
  raise typer.Exit(0)
306
438
 
307
439
 
@@ -327,7 +459,7 @@ def stop_container(
327
459
  check_command_permission(command_name)
328
460
 
329
461
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
330
- typer.echo("Please provide a single identifier for container - ID or name.")
462
+ typer.echo("Provide a single identifier for container - ID or name.")
331
463
  raise typer.Exit(1)
332
464
 
333
465
  service_factory = validate_config_and_get_service_factory()
@@ -339,10 +471,47 @@ def stop_container(
339
471
  action=DockerContainerAction.STOP
340
472
  )
341
473
 
342
- app_logger.info(f'Container stop completed')
474
+ app_logger.info(f'Container is stopping')
343
475
  raise typer.Exit(0)
344
476
 
345
477
 
478
+ @app.command(name="delete", no_args_is_help=True, help=__("Delete container"), **get_command_metadata(CliCommand.CONTAINER_DELETE))
479
+ def delete_container(
480
+ container_public_id: str = typer.Option(
481
+ None,
482
+ '--container-id',
483
+ '-cid',
484
+ help=__("Container ID"),
485
+ is_eager=False,
486
+ ),
487
+ container_slug: str = typer.Option(
488
+ None,
489
+ '--container-name',
490
+ '-cn',
491
+ help=__("Container name"),
492
+ is_eager=False,
493
+ ),
494
+ ):
495
+ command_name = CliCommand.CONTAINER_DELETE
496
+ app_logger.info(f'Running {command_name} from {get_current_directory()}')
497
+ check_command_permission(command_name)
498
+
499
+ if sum(v is not None for v in [container_public_id, container_slug]) != 1:
500
+ typer.echo("Provide a single identifier for container - ID or name.")
501
+ raise typer.Exit(1)
502
+
503
+ service_factory = validate_config_and_get_service_factory()
504
+ container_service: ContainerService = service_factory.get_container_service()
505
+
506
+ container_service.request_docker_container_action(
507
+ container_public_id=container_public_id,
508
+ container_slug=container_slug,
509
+ action=DockerContainerAction.DELETE
510
+ )
511
+
512
+ app_logger.info(f'Container is deleting')
513
+ raise typer.Exit(0)
514
+
346
515
  @app.command(name="restart", no_args_is_help=True, help=__("Restart container"), **get_command_metadata(CliCommand.CONTAINER_RESTART))
347
516
  def restart_container(
348
517
  container_public_id: str = typer.Option(
@@ -365,7 +534,7 @@ def restart_container(
365
534
  check_command_permission(command_name)
366
535
 
367
536
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
368
- typer.echo("Please provide a single identifier for container - ID or name.")
537
+ typer.echo("Provide a single identifier for container - ID or name.")
369
538
  raise typer.Exit(1)
370
539
 
371
540
  service_factory = validate_config_and_get_service_factory()
@@ -377,7 +546,7 @@ def restart_container(
377
546
  action=DockerContainerAction.RESTART
378
547
  )
379
548
 
380
- app_logger.info(f'Container restart completed')
549
+ app_logger.info(f'Container is restarting')
381
550
  raise typer.Exit(0)
382
551
 
383
552
 
@@ -410,7 +579,7 @@ def container_logs(
410
579
  check_command_permission(command_name)
411
580
 
412
581
  if sum(v is not None for v in [container_public_id, container_slug]) != 1:
413
- typer.echo("Please provide a single identifier for container - ID or name.")
582
+ typer.echo("Provide a single identifier for container - ID or name.")
414
583
  raise typer.Exit(1)
415
584
 
416
585
  service_factory = validate_config_and_get_service_factory()
@@ -425,4 +594,4 @@ def container_logs(
425
594
  logging_service.print_last_container_logs(container_public_id=container_public_id, container_slug=container_slug, logs_number=logs_number)
426
595
 
427
596
  app_logger.info(f'Container log streaming completed')
428
- raise typer.Exit(0)
597
+ raise typer.Exit(0)
@@ -1,6 +1,10 @@
1
1
  from typing import Optional, List, Dict
2
2
 
3
3
  from thestage.config.business.config_provider import ConfigProvider
4
+ from thestage.docker_container.dto.docker_container_create_request import DockerContainerCreateRequest
5
+ from thestage.docker_container.dto.docker_container_create_response import DockerContainerCreateResult
6
+ from thestage.docker_container.dto.docker_image_list_request import DockerImageListRequest
7
+ from thestage.docker_container.dto.docker_image_list_response import DockerImageListResponse
4
8
  from thestage.global_dto.enums.order_direction_type import OrderDirectionType
5
9
  from thestage.services.clients.thestage_api.core.api_client_core import TheStageApiClientCore
6
10
  from thestage.services.clients.thestage_api.dtos.base_response import TheStageBaseResponse
@@ -11,7 +15,7 @@ from thestage.docker_container.dto.container_action_request import DockerContain
11
15
  from thestage.docker_container.dto.container_response import (
12
16
  DockerContainerDto,
13
17
  DockerContainerViewResponse,
14
- ContainerBusinessStatusMapperResponse
18
+ ContainerBusinessStatusMapperResponse, DockerImageDto
15
19
  )
16
20
  from thestage.docker_container.dto.docker_container_list_request import DockerContainerListRequest
17
21
  from thestage.docker_container.dto.docker_container_list_response import DockerContainerListResponse
@@ -50,9 +54,32 @@ class DockerContainerApiClient(TheStageApiClientCore):
50
54
  )
51
55
 
52
56
  result = DockerContainerListResponse.model_validate(response) if response else None
53
- # return result.paginatedList.entities, result.paginatedList.pagination_data.total_pages if result and result.is_success else None
54
57
  return result.paginatedList if result and result.is_success else None
55
58
 
59
+
60
+ def get_docker_image_list(
61
+ self,
62
+ page: int = 1,
63
+ limit: int = 10,
64
+ ) -> PaginatedEntityList[DockerImageDto]:
65
+ request = DockerImageListRequest(
66
+ entityFilterRequest=EntityFilterRequest(
67
+ orderByField="createdAt",
68
+ orderByDirection=OrderDirectionType.DESC,
69
+ page=page,
70
+ limit=limit,
71
+ ),
72
+ )
73
+ response = self._request(
74
+ method='POST',
75
+ url='/user-api/v1/docker-image/list',
76
+ data=request.model_dump(),
77
+ token=self.__config_provider.get_config().main.thestage_auth_token
78
+ )
79
+ result = DockerImageListResponse.model_validate(response) if response else None
80
+ return result.paginatedList if result and result.is_success else None
81
+
82
+
56
83
  def get_container(
57
84
  self,
58
85
  container_slug: Optional[str] = None,
@@ -72,6 +99,18 @@ class DockerContainerApiClient(TheStageApiClientCore):
72
99
 
73
100
  return DockerContainerViewResponse.model_validate(response).docker_container if response else None
74
101
 
102
+
103
+ def create_container(self, request_param: DockerContainerCreateRequest) -> Optional[DockerContainerCreateResult]:
104
+ response = self._request(
105
+ method='POST',
106
+ url='/user-api/v1/docker-container/create',
107
+ data=request_param.model_dump(by_alias=True),
108
+ token=self.__config_provider.get_config().main.thestage_auth_token
109
+ )
110
+
111
+ return DockerContainerCreateResult.model_validate(response) if response else None
112
+
113
+
75
114
  def container_action(
76
115
  self,
77
116
  request_param: DockerContainerActionRequest,
@@ -87,6 +126,7 @@ class DockerContainerApiClient(TheStageApiClientCore):
87
126
  result = TheStageBaseResponse.model_validate(response) if response else None
88
127
  return result
89
128
 
129
+
90
130
  def get_container_business_status_map(self) -> Optional[Dict[str, str]]:
91
131
  response = self._request(
92
132
  method='POST',
@@ -0,0 +1,40 @@
1
+ import typer
2
+
3
+ from thestage.cli_command import CliCommand
4
+ from thestage.cli_command_helper import get_command_metadata, check_command_permission
5
+ from thestage.controllers.utils_controller import validate_config_and_get_service_factory
6
+ from thestage.docker_container.business.container_service import ContainerService
7
+ from thestage.i18n.translation import __
8
+
9
+ app = typer.Typer(no_args_is_help=True, help=__("Manage Docker images"))
10
+
11
+ @app.command(name='ls', help=__("List available docker images"), **get_command_metadata(CliCommand.CONTAINER_LS))
12
+ def list_available_images(
13
+ row: int = typer.Option(
14
+ 5,
15
+ '--row',
16
+ '-r',
17
+ help=__("Set number of rows displayed per page"),
18
+ is_eager=False,
19
+ ),
20
+ page: int = typer.Option(
21
+ 1,
22
+ '--page',
23
+ '-p',
24
+ help=__("Set starting page for displaying output"),
25
+ is_eager=False,
26
+ ),
27
+ ):
28
+ command_name = CliCommand.CONTAINER_IMAGE_LS
29
+ check_command_permission(command_name)
30
+
31
+ service_factory = validate_config_and_get_service_factory()
32
+ container_service: ContainerService = service_factory.get_container_service()
33
+
34
+ container_service.print_docker_image_list(
35
+ row=row,
36
+ page=page,
37
+ )
38
+
39
+ typer.echo(__("Images listing complete"))
40
+ raise typer.Exit(0)
@@ -45,3 +45,10 @@ class ContainerBusinessStatusMapperResponse(TheStageBasePaginatedResponse):
45
45
  model_config = ConfigDict(use_enum_values=True)
46
46
 
47
47
  docker_container_status_map: Dict[str, str] = Field(default={}, alias='dockerContainerStatusMap')
48
+
49
+ class DockerImageDto(BaseModel):
50
+ model_config = ConfigDict(use_enum_values=True)
51
+
52
+ id: Optional[int] = Field(None, alias='id')
53
+ image: Optional[str] = Field(None, alias='image')
54
+ tag: Optional[str] = Field(None, alias='tag')
@@ -0,0 +1,28 @@
1
+ from typing import Optional, Dict
2
+
3
+ from pydantic import BaseModel, Field, ConfigDict
4
+
5
+
6
+ class DockerContainerMappings(BaseModel):
7
+ model_config = ConfigDict(populate_by_name=True)
8
+
9
+ directory_mappings: Dict[str, str] = Field(default_factory=dict, alias='directoryMappings')
10
+ port_mappings: Dict[str, str] = Field(default_factory=dict, alias='portMappings')
11
+
12
+
13
+ class DockerContainerCreateRequest(BaseModel):
14
+ model_config = ConfigDict(populate_by_name=True)
15
+
16
+ instance_rented_public_id: Optional[str] = Field(None, alias='instanceRentedPublicId')
17
+ instance_rented_slug: Optional[str] = Field(None, alias='instanceRentedSlug')
18
+ selfhosted_instance_public_id: Optional[str] = Field(None, alias='selfhostedInstancePublicId')
19
+ selfhosted_instance_slug: Optional[str] = Field(None, alias='selfhostedInstanceSlug')
20
+ project_public_id: Optional[str] = Field(..., alias='projectPublicId')
21
+ project_slug: Optional[str] = Field(..., alias='projectSlug')
22
+ container_slug: str = Field(..., alias='containerSlug')
23
+ docker_image_id: int = Field(..., alias='dockerImageId')
24
+ assigned_cpus: Optional[int] = Field(None, alias='assignedCpus')
25
+ is_unlimited_cpus: bool = Field(False, alias='isUnlimitedCpus')
26
+ assigned_gpus: Optional[int] = Field(None, alias='assignedGpus')
27
+ is_unlimited_gpus: bool = Field(False, alias='isUnlimitedGpus')
28
+ mappings: DockerContainerMappings = Field(default_factory=DockerContainerMappings, alias='mappings')
@@ -0,0 +1,11 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import Field, ConfigDict
4
+
5
+ from thestage.services.clients.thestage_api.dtos.base_response import TheStageBaseResponse
6
+
7
+
8
+ class DockerContainerCreateResult(TheStageBaseResponse):
9
+ model_config = ConfigDict(populate_by_name=True)
10
+
11
+ docker_container_public_id: Optional[str] = Field(None, alias='dockerContainerPublicId')
@@ -0,0 +1,11 @@
1
+ from typing import Optional, List
2
+
3
+ from pydantic import Field, ConfigDict, BaseModel
4
+
5
+ from thestage.services.clients.thestage_api.dtos.entity_filter_request import EntityFilterRequest
6
+
7
+
8
+ class DockerImageListRequest(BaseModel):
9
+ model_config = ConfigDict(use_enum_values=True)
10
+
11
+ entityFilterRequest: EntityFilterRequest = Field(None, alias='entityFilterRequest')
@@ -0,0 +1,13 @@
1
+ from typing import List, Optional
2
+
3
+ from pydantic import Field, ConfigDict
4
+
5
+ from thestage.docker_container.dto.container_response import DockerImageDto
6
+ from thestage.services.clients.thestage_api.dtos.base_response import TheStageBaseResponse
7
+ from thestage.services.clients.thestage_api.dtos.paginated_entity_list import PaginatedEntityList
8
+
9
+
10
+ class DockerImageListResponse(TheStageBaseResponse):
11
+ model_config = ConfigDict(use_enum_values=True)
12
+
13
+ paginatedList: PaginatedEntityList[DockerImageDto] = Field(None, alias='paginatedList')
@@ -0,0 +1,14 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel, ConfigDict, Field
4
+
5
+
6
+ class DockerImageEntity(BaseModel):
7
+
8
+ model_config = ConfigDict(
9
+ populate_by_name=True,
10
+ use_enum_values=True,
11
+ )
12
+
13
+ image: Optional[str] = Field(None, alias='IMAGE')
14
+ tag: Optional[str] = Field(None, alias='TAG')
@@ -107,7 +107,7 @@ def error_handler() -> Callable:
107
107
  else:
108
108
  typer.echo(__('Undefined error occurred'))
109
109
  # typer.echo(e100.__class__.__name__)
110
- # print(traceback.format_exc())
110
+ print(traceback.format_exc())
111
111
  # TODO send all exceptions to backend?
112
112
  app_logger.error(f'{traceback.format_exc()}')
113
113
  raise typer.Exit(1)
@@ -69,7 +69,7 @@ class InferenceModelService(AbstractService):
69
69
  project_config: ProjectConfig = self.__project_service.get_fixed_project_config()
70
70
  if not project_config:
71
71
  typer.echo(
72
- __("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
72
+ __("No project found at the path: %path%. Initialize or clone a project first. Or provide path to project using --working-directory option.",
73
73
  {"path": config.runtime.working_directory}))
74
74
  raise typer.Exit(1)
75
75
 
@@ -77,12 +77,12 @@ class InferenceModelService(AbstractService):
77
77
  [rented_instance_public_id, rented_instance_slug, self_hosted_instance_public_id,
78
78
  self_hosted_instance_slug])
79
79
  if instance_args_count != 1:
80
- typer.echo("Please provide a single instance (rented or self-hosted) identifier - name or ID.")
80
+ typer.echo("Provide a single instance (rented or self-hosted) identifier - name or ID.")
81
81
  raise typer.Exit(1)
82
82
 
83
83
  model_args_count = sum(v is not None for v in [model_public_id, model_slug])
84
84
  if model_args_count != 1:
85
- typer.echo("Please provide a single model identifier - name or ID.")
85
+ typer.echo("Provide a single model identifier - name or ID.")
86
86
  raise typer.Exit(1)
87
87
 
88
88
  typer.echo(f"Creating inference simulator")
@@ -126,7 +126,7 @@ class InferenceModelService(AbstractService):
126
126
  project_config: ProjectConfig = self.__project_service.get_fixed_project_config()
127
127
  if not project_config:
128
128
  typer.echo(
129
- __("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
129
+ __("No project found at the path: %path%. Initialize or clone a project first. Or provide path to project using --working-directory option.",
130
130
  {"path": config.runtime.working_directory}))
131
131
  raise typer.Exit(1)
132
132
 
@@ -144,7 +144,7 @@ class InferenceModelService(AbstractService):
144
144
 
145
145
  project_config: ProjectConfig = self.__config_provider.read_project_config()
146
146
  if not project_config:
147
- typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.",
147
+ typer.echo(__("No project found at the path: %path%. Initialize or clone a project first.",
148
148
  {"path": config.runtime.working_directory}))
149
149
  raise typer.Exit(1)
150
150
 
@@ -233,7 +233,7 @@ class InferenceModelService(AbstractService):
233
233
  if not project_public_id and not project_slug:
234
234
  project_config: ProjectConfig = self.__config_provider.read_project_config()
235
235
  if not project_config:
236
- typer.echo(__("Provide the project unique ID or run this command from within an initialized project directory"))
236
+ typer.echo(__("Provide the project unique ID or name, or run this command from within an initialized project directory"))
237
237
  raise typer.Exit(1)
238
238
  project_public_id = project_config.public_id
239
239
 
@@ -64,13 +64,13 @@ class InferenceSimulatorService(AbstractService):
64
64
  project_config: ProjectConfig = self.__project_service.get_fixed_project_config()
65
65
 
66
66
  if not project_config:
67
- typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first. Or provide path to project using --working-directory option.",
67
+ typer.echo(__("No project found at the path: %path%. Initialize or clone a project first. Or provide path to project using --working-directory option.",
68
68
  {"path": config.runtime.working_directory}))
69
69
  raise typer.Exit(1)
70
70
 
71
71
  instance_args_count = sum(v is not None for v in [rented_instance_public_id, rented_instance_slug, self_hosted_instance_public_id, self_hosted_instance_slug])
72
72
  if instance_args_count != 1:
73
- typer.echo("Please provide a single instance (rented or self-hosted) identifier - name or ID.")
73
+ typer.echo("Provide a single instance (rented or self-hosted) identifier - name or ID.")
74
74
  raise typer.Exit(1)
75
75
 
76
76
  has_wrong_args = files_to_add and commit_hash or is_skip_auto_commit and commit_hash or files_to_add and is_skip_auto_commit
@@ -141,7 +141,7 @@ class InferenceSimulatorService(AbstractService):
141
141
  raise typer.Exit(0)
142
142
 
143
143
  commit_name = typer.prompt(
144
- text=__('Please provide commit message'),
144
+ text=__('Provide commit message'),
145
145
  show_choices=False,
146
146
  type=str,
147
147
  show_default=False,
thestage/main.py CHANGED
@@ -31,6 +31,7 @@ def main():
31
31
  from thestage.task.communication import task_command
32
32
  from thestage.instance.communication import instance_command
33
33
  from thestage.docker_container.communication import docker_command
34
+ from thestage.docker_container.communication import image_command
34
35
 
35
36
  project_command.app.add_typer(
36
37
  inference_simulator_command.app,
@@ -48,6 +49,12 @@ def main():
48
49
  rich_help_panel=get_command_group_help_panel()
49
50
  )
50
51
 
52
+ docker_command.app.add_typer(
53
+ image_command.app,
54
+ name="image",
55
+ rich_help_panel=get_command_group_help_panel()
56
+ )
57
+
51
58
  base_controller.app.add_typer(
52
59
  project_command.app,
53
60
  name="project",
@@ -282,7 +282,7 @@ class ProjectService(AbstractService):
282
282
  raise typer.Exit(0)
283
283
 
284
284
  commit_name = typer.prompt(
285
- text=__('Please provide commit message'),
285
+ text=__('Provide commit message'),
286
286
  show_choices=False,
287
287
  type=str,
288
288
  show_default=False,
@@ -448,7 +448,7 @@ class ProjectService(AbstractService):
448
448
  config = self.__config_provider.get_config()
449
449
  project_config: ProjectConfig = self.get_fixed_project_config()
450
450
  if not project_config:
451
- typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.",
451
+ typer.echo(__("No project found at the path: %path%. Initialize or clone a project first.",
452
452
  {"path": config.runtime.working_directory}))
453
453
  raise typer.Exit(1)
454
454
 
@@ -463,7 +463,7 @@ class ProjectService(AbstractService):
463
463
  config = self.__config_provider.get_config()
464
464
  project_config: ProjectConfig = self.get_fixed_project_config()
465
465
  if not project_config:
466
- typer.echo(__("No project found at the path: %path%. Please initialize or clone a project first.",
466
+ typer.echo(__("No project found at the path: %path%. Initialize or clone a project first.",
467
467
  {"path": config.runtime.working_directory}))
468
468
  raise typer.Exit(1)
469
469
 
@@ -97,8 +97,11 @@ class TaskService(AbstractService):
97
97
  if (project_config.prompt_for_default_container is None or project_config.prompt_for_default_container) and (
98
98
  docker_container_slug or docker_container_public_id) and (
99
99
  project_config.default_container_public_id != container.public_id):
100
+
101
+ container_display_name = docker_container_slug or docker_container_public_id
102
+
100
103
  set_default_container_answer: str = typer.prompt(
101
- text=f"Would you like to set '{docker_container_slug}' as a default container for this project installation?",
104
+ text=f"Would you like to set '{container_display_name}' as a default container for this project installation?",
102
105
  show_choices=True,
103
106
  default=YesOrNoResponse.YES.value,
104
107
  type=click.Choice([r.value for r in YesOrNoResponse]),
@@ -181,7 +184,7 @@ class TaskService(AbstractService):
181
184
  raise typer.Exit(0)
182
185
 
183
186
  commit_name = typer.prompt(
184
- text=__('Please provide commit message'),
187
+ text=__('Provide commit message'),
185
188
  show_choices=False,
186
189
  type=str,
187
190
  show_default=False,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: thestage
3
- Version: 0.6.8
3
+ Version: 0.7.0
4
4
  Summary:
5
5
  License-File: LICENSE.txt
6
6
  Author: TheStage AI team
@@ -1,7 +1,7 @@
1
1
  thestage/.env,sha256=lHeR-H4TiBp4z06bWt7oND9LMMVXP3WCKY7lQltDM3M,129
2
- thestage/__init__.py,sha256=RtJQViaAdxMvBYnV43lQnl8Hz4nwS5r6TRtt-oVNv3U,64
2
+ thestage/__init__.py,sha256=gOoetVwGi8lg544YyJt-0IW8GNyiFt9ED5q17C7oM_s,64
3
3
  thestage/__main__.py,sha256=4ObdWrDRaIASaR06IxtFSsoMu58eyL0MnD64habvPj8,101
4
- thestage/cli_command.py,sha256=P93LXDruJWecmF5xn-D2uhMOSD9iSJl-gP4oG9aD56k,2173
4
+ thestage/cli_command.py,sha256=9RTeALR2soUXL9poFY_CRiOOXdE9kKbqchOmZPtGDC4,2303
5
5
  thestage/cli_command_helper.py,sha256=htwALwdgI1CQvq-tsyTk6lA4_sc5hsOUpECrMNSdDm0,1954
6
6
  thestage/color_scheme/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  thestage/color_scheme/color_scheme.py,sha256=Wex4gj3bS1vh-IO4fGT0NPaHC-PNpbHSiFfL2VTmT-U,232
@@ -37,22 +37,29 @@ thestage/controllers/utils_controller.py,sha256=ac-TLWC2wrmxJYNFOltoQKCTpFaKB6p9
37
37
  thestage/debug_main.dist.py,sha256=FPN2UCtWmAlvszFW217iwD8hEgxCOhmCcp1zRiGmS6A,1001
38
38
  thestage/docker_container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
39
  thestage/docker_container/business/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
40
- thestage/docker_container/business/container_service.py,sha256=vzJtPe-GZvRIHcYTn6U41CK3KIIYmhg_NuYae-BjFJ8,18505
40
+ thestage/docker_container/business/container_service.py,sha256=VmajD5DjtBjZJKpHw5srq5r59-5C98mJgOiWgEqdV1s,23084
41
41
  thestage/docker_container/business/mapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
42
  thestage/docker_container/business/mapper/container_mapper.py,sha256=WJCmPLsDlWGYKejZkS2BBnh_i4vBd_WkeIc0CSpCjV0,1162
43
+ thestage/docker_container/business/mapper/image_mapper.py,sha256=DAoVomo6Av-YUywzR-f7zVTXuvJqLx7V1vFvk0zj_2Y,515
43
44
  thestage/docker_container/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- thestage/docker_container/communication/docker_command.py,sha256=Z3N99MtDgwlEvyYyclsvgrm663zi8rDD8IgY48pIZQU,16138
45
- thestage/docker_container/communication/docker_container_api_client.py,sha256=eYlGO9TvnBRPwqn3tcKR4wXlYqNUo8wcYWYbioFIonM,4175
45
+ thestage/docker_container/communication/docker_command.py,sha256=EHD-s1S9_xjwJBiCzrZoAR3P3rP5GHPi1y5oY0jU9VI,22464
46
+ thestage/docker_container/communication/docker_container_api_client.py,sha256=JBwNcr1V3zrav9V27_1CHg6p1gZ4GDpVa2qxbOo6Iy4,5754
47
+ thestage/docker_container/communication/image_command.py,sha256=96anKESWLFRVAqJzNZVOnUt_Fx-yMGvvU7BkXqZcCpk,1356
46
48
  thestage/docker_container/dto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
49
  thestage/docker_container/dto/container_action_request.py,sha256=nm9Spe0zDmSjmHwls9rajtTT2oNLkUqwYjZHGC5HpMw,429
48
50
  thestage/docker_container/dto/container_entity.py,sha256=k9A04P9WSrZJPU-SqJ7Ld0sxFaFJvWmFMlBImyHkHbk,660
49
- thestage/docker_container/dto/container_response.py,sha256=CXVLiQCmqbgQ34E0el843w28qzqW5ZrrvuwWNxayj50,2364
51
+ thestage/docker_container/dto/container_response.py,sha256=9QmQCiNq9iFRDybZkOjEEswAtroX4eCT319qohQ50po,2602
52
+ thestage/docker_container/dto/docker_container_create_request.py,sha256=xSfj1l6PWdgdHp-hGsuOCxQnUL7KGuzqFfoe2NPJ58I,1471
53
+ thestage/docker_container/dto/docker_container_create_response.py,sha256=zfpdhQ9Luy80a8MIJmjNJxfoizy5R-NIOSJBopeG5ew,366
50
54
  thestage/docker_container/dto/docker_container_list_request.py,sha256=iJndw9cmH8cXMP-pzwmrU76xBMiMI8auAg3--TWBNcM,578
51
55
  thestage/docker_container/dto/docker_container_list_response.py,sha256=En3psvip3eBJO_0-AEUDwXO6J3cmTqPb7cvlDyZWpww,517
52
56
  thestage/docker_container/dto/docker_container_mapping.py,sha256=pH0lCKTDheDI16NasqQiF288szUgpw758W27JrJo0QI,372
57
+ thestage/docker_container/dto/docker_image_list_request.py,sha256=6MvvHLMyfXfJFJ7mZJO3yBB64wT7K3rXyuv4_IHy-dA,368
58
+ thestage/docker_container/dto/docker_image_list_response.py,sha256=Z4GrGx4QlouzhaYvvivpFTBnta-akoWGj-ongM6WpNA,540
53
59
  thestage/docker_container/dto/enum/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
60
  thestage/docker_container/dto/enum/container_pending_action.py,sha256=7DMt4KdpO91-NnU-Rb2s8ikDpt3IxsyBYEH1lWhR9MI,222
55
61
  thestage/docker_container/dto/enum/container_status.py,sha256=CpXOUJgpqKMRW7QKyJSeegtj_aQBOfDcNsvztMAJvzw,457
62
+ thestage/docker_container/dto/image_entity.py,sha256=jcBZ_IOOxpwGcI785KE3TkEb6jr2kXSScJfAGzPOQlI,321
56
63
  thestage/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
64
  thestage/exceptions/auth_exception.py,sha256=HZeiF3aocqukmClUfIDCk5izb4tpG4KEbhyXtVl2z34,214
58
65
  thestage/exceptions/base_exception.py,sha256=ekov63Q0OZLeLOu6bhHN9KLo8pPn0XLCMZwZrVULhDw,282
@@ -75,7 +82,7 @@ thestage/global_dto/enums/tail_output_type.py,sha256=6S55VRRosrI3yZW8XeAGm4u12H4
75
82
  thestage/global_dto/enums/yes_no_response.py,sha256=PXXkB7KMB-XCHvKOBRuX6NRKyq8UW0G228OICRr2ZTk,86
76
83
  thestage/global_dto/file_item.py,sha256=hxUbdne6AYFHJfuQ8l0XQO86IuC8Pyx_OrPw_0lhN_o,828
77
84
  thestage/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
- thestage/helpers/error_handler.py,sha256=LT3JspQF5hf07NJvsZErgzFRnN7GTQA_4_XH30tt9dY,5375
85
+ thestage/helpers/error_handler.py,sha256=hFplj8ZCixlUVQHcyuAQU0N-F7uFzRtExGb3ceII6Ik,5373
79
86
  thestage/helpers/exception_hook.py,sha256=jekDs7smzI1UjXkuMut_uM725GL3c6Gr_zmyEYdDjV8,334
80
87
  thestage/helpers/logger/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
88
  thestage/helpers/logger/app_logger.py,sha256=JA8mn4eD39ouNbr4HOFN-H8vXphyzobjXQNdI0-GEWI,1588
@@ -84,7 +91,7 @@ thestage/i18n/en_GB/messages.po,sha256=Yue25UrRoPtDpVN14xrdnwOdtxHJz-m-9VMcFIczY
84
91
  thestage/i18n/translation.py,sha256=c62OicQ4phSMuqDe7hqGebIsk0W2-8ZJUfgfdtjjqEc,284
85
92
  thestage/inference_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
93
  thestage/inference_model/business/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
87
- thestage/inference_model/business/inference_model_service.py,sha256=VV9n8Jbc2n8jI2nII1Z94lQ7KeHFo2qBQxbCn_Mbxfk,12982
94
+ thestage/inference_model/business/inference_model_service.py,sha256=ZERbi1LV5LCB_3H80gVGN1Jm32X6VvtYuKXE-nKACNM,12956
88
95
  thestage/inference_model/business/mapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
96
  thestage/inference_model/business/mapper/inference_model_mapper.py,sha256=ez1kiKbUeRmvz343NPBID8hD3G9BS_-YOiWXDkSjBGg,821
90
97
  thestage/inference_model/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -106,7 +113,7 @@ thestage/inference_model/dto/push_inference_simulator_model_request.py,sha256=xF
106
113
  thestage/inference_model/dto/push_inference_simulator_model_response.py,sha256=HJA-gS23xCErBgKcyIoJetOlHuL-Ui5mPoxBMFPnvtg,242
107
114
  thestage/inference_simulator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
115
  thestage/inference_simulator/business/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
109
- thestage/inference_simulator/business/inference_simulator_service.py,sha256=sXs4pKFbZjtTWHZqolhcvBywbSdvPuXzGICoJJKtctI,16730
116
+ thestage/inference_simulator/business/inference_simulator_service.py,sha256=Zz1RjjzwFyP5PQNYgfrflZG94YjrKJlAjGentdUoofI,16709
110
117
  thestage/inference_simulator/business/mapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
111
118
  thestage/inference_simulator/business/mapper/inference_simulator_mapper.py,sha256=5DwYbVM4hGpbjNYJqzptKWJIIGJmPFrmTxi5bvj89ko,835
112
119
  thestage/inference_simulator/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -161,10 +168,10 @@ thestage/logging/dto/task_log_stream_request.py,sha256=fbqteayY0XR7wNjuSrvJNJ61H
161
168
  thestage/logging/dto/user_logs_query_request.py,sha256=7bObMnRtSvMtqNJopsOz5VkxMBJ5Be2ksTdi-VWdMuE,542
162
169
  thestage/logging/dto/user_logs_query_response.py,sha256=gLSTkt3UxU8kFQwN4915XcwziGo3c1kV6FusVkd1M0o,539
163
170
  thestage/logging/logging_constants.py,sha256=4Gk2tglHW_-jnjB8uVIh-ds4fAVBqNW8igfQt8k7Quc,137
164
- thestage/main.py,sha256=qqGFn-iD2Yq1deAbcXO2Yrx7T7rrcGu6VVci5YoQU6Y,2981
171
+ thestage/main.py,sha256=U8Fg7PvrdDwVPY_CU9c1arNVmz1JPvhnsjB5KgUoH0o,3220
165
172
  thestage/project/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
173
  thestage/project/business/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
167
- thestage/project/business/project_service.py,sha256=yNC6zoejcNoj_57uZgMhUY2sRvrQ7cmkqH1WtWXimuk,21673
174
+ thestage/project/business/project_service.py,sha256=VM_5uSwzV-AO1Qpqk_OvRrKaAwLjHkKh4ILshhpMdiw,21652
168
175
  thestage/project/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
169
176
  thestage/project/communication/project_api_client.py,sha256=EYb94JTmPR4GEp1BVCd5K_s5DU9hrqs1EZRURMBpJq8,1927
170
177
  thestage/project/communication/project_command.py,sha256=z0ForJdhiKzx6S1KGXVy-z-TCYAHJXs9fw5zOI1RxSw,10560
@@ -196,7 +203,7 @@ thestage/task/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
196
203
  thestage/task/business/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
197
204
  thestage/task/business/mapper/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
205
  thestage/task/business/mapper/task_mapper.py,sha256=4ovqqGPMRUV4WY9ggVmEfeNhpgkA2hbnPNHCDEZRGj8,679
199
- thestage/task/business/task_service.py,sha256=qnbamOmnomtMdtoazaMesNl86KTe0mbMKi0H2CNieCw,15118
206
+ thestage/task/business/task_service.py,sha256=2ogaoqXNozdWpX2BRZU9UaJNCR3cu7BNNpY3iCfjVzQ,15203
200
207
  thestage/task/communication/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
208
  thestage/task/communication/task_api_client.py,sha256=R6a40_BFef5L9nxcvQN5nEyqJOKgeDXWrRtrOoLwuEk,4663
202
209
  thestage/task/communication/task_command.py,sha256=nhz13iJDwyeYNpx4Vz9ervQrSC2GTbg-78uGEtFz1Po,7747
@@ -212,8 +219,8 @@ thestage/task/dto/status_localized_map_response.py,sha256=yop410FwNvQFXYHKh60ne4
212
219
  thestage/task/dto/task.py,sha256=hN2jx7RWKv65Jfv6onv7tqLxtJgZ3TuDxFj2512ALwA,726
213
220
  thestage/task/dto/task_entity.py,sha256=k2nJ5ewU1BMPqkvewm1TfPsTlvPjgV6Ke0QYYim_dQM,587
214
221
  thestage/task/dto/view_response.py,sha256=YMO1nbpBeLDMyW15g0BIEGINAqtBYECPqILJbdgdkmE,289
215
- thestage-0.6.8.dist-info/METADATA,sha256=niUim0c14ngruKCPwvi6fpL6IF0VL3wiZ6twAjfmOvE,5622
216
- thestage-0.6.8.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
217
- thestage-0.6.8.dist-info/entry_points.txt,sha256=57pMhs8zaCM-jgeTffC0WVqCsh35Uq_dUDmzXR80CI4,47
218
- thestage-0.6.8.dist-info/licenses/LICENSE.txt,sha256=U9QrxfdD7Ie7r8z1FleuvOGQvgCF1m0Mjd78cFvWaHE,572
219
- thestage-0.6.8.dist-info/RECORD,,
222
+ thestage-0.7.0.dist-info/METADATA,sha256=PMvOR4NGwNGUh2c5b_oGf5cxkItLNCp82ZhwjJW8BQI,5622
223
+ thestage-0.7.0.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
224
+ thestage-0.7.0.dist-info/entry_points.txt,sha256=57pMhs8zaCM-jgeTffC0WVqCsh35Uq_dUDmzXR80CI4,47
225
+ thestage-0.7.0.dist-info/licenses/LICENSE.txt,sha256=U9QrxfdD7Ie7r8z1FleuvOGQvgCF1m0Mjd78cFvWaHE,572
226
+ thestage-0.7.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.3.0
2
+ Generator: poetry-core 2.3.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any