cosmotech-acceleration-library 1.1.0__py3-none-any.whl → 2.1.0rc1__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.
Files changed (120) hide show
  1. cosmotech/coal/__init__.py +1 -1
  2. cosmotech/coal/aws/__init__.py +1 -9
  3. cosmotech/coal/aws/s3.py +181 -214
  4. cosmotech/coal/azure/__init__.py +5 -5
  5. cosmotech/coal/azure/adx/__init__.py +24 -10
  6. cosmotech/coal/azure/adx/auth.py +2 -2
  7. cosmotech/coal/azure/adx/ingestion.py +10 -14
  8. cosmotech/coal/azure/adx/query.py +1 -1
  9. cosmotech/coal/azure/adx/runner.py +13 -14
  10. cosmotech/coal/azure/adx/store.py +5 -86
  11. cosmotech/coal/azure/adx/tables.py +2 -2
  12. cosmotech/coal/azure/adx/utils.py +2 -2
  13. cosmotech/coal/azure/blob.py +20 -26
  14. cosmotech/coal/azure/storage.py +3 -3
  15. cosmotech/coal/cosmotech_api/__init__.py +0 -28
  16. cosmotech/coal/cosmotech_api/apis/__init__.py +14 -0
  17. cosmotech/coal/cosmotech_api/apis/dataset.py +222 -0
  18. cosmotech/coal/cosmotech_api/apis/meta.py +25 -0
  19. cosmotech/coal/cosmotech_api/apis/organization.py +24 -0
  20. cosmotech/coal/cosmotech_api/apis/run.py +38 -0
  21. cosmotech/coal/cosmotech_api/apis/runner.py +75 -0
  22. cosmotech/coal/cosmotech_api/apis/solution.py +23 -0
  23. cosmotech/coal/cosmotech_api/apis/workspace.py +108 -0
  24. cosmotech/coal/cosmotech_api/objects/__init__.py +9 -0
  25. cosmotech/coal/cosmotech_api/objects/connection.py +125 -0
  26. cosmotech/coal/cosmotech_api/objects/parameters.py +127 -0
  27. cosmotech/coal/postgresql/runner.py +58 -41
  28. cosmotech/coal/postgresql/store.py +56 -15
  29. cosmotech/coal/postgresql/utils.py +255 -0
  30. cosmotech/coal/singlestore/store.py +3 -2
  31. cosmotech/coal/store/__init__.py +16 -13
  32. cosmotech/coal/store/output/__init__.py +0 -0
  33. cosmotech/coal/store/output/aws_channel.py +74 -0
  34. cosmotech/coal/store/output/az_storage_channel.py +33 -0
  35. cosmotech/coal/store/output/channel_interface.py +38 -0
  36. cosmotech/coal/store/output/channel_spliter.py +61 -0
  37. cosmotech/coal/store/output/postgres_channel.py +37 -0
  38. cosmotech/coal/store/pandas.py +1 -1
  39. cosmotech/coal/store/pyarrow.py +2 -2
  40. cosmotech/coal/store/store.py +4 -7
  41. cosmotech/coal/utils/configuration.py +197 -0
  42. cosmotech/coal/utils/decorator.py +4 -7
  43. cosmotech/csm_data/commands/adx_send_data.py +1 -1
  44. cosmotech/csm_data/commands/adx_send_runnerdata.py +3 -2
  45. cosmotech/csm_data/commands/api/api.py +6 -19
  46. cosmotech/csm_data/commands/api/postgres_send_runner_metadata.py +20 -16
  47. cosmotech/csm_data/commands/api/run_load_data.py +15 -52
  48. cosmotech/csm_data/commands/api/wsf_load_file.py +13 -16
  49. cosmotech/csm_data/commands/api/wsf_send_file.py +11 -14
  50. cosmotech/csm_data/commands/az_storage_upload.py +3 -2
  51. cosmotech/csm_data/commands/s3_bucket_delete.py +16 -15
  52. cosmotech/csm_data/commands/s3_bucket_download.py +16 -16
  53. cosmotech/csm_data/commands/s3_bucket_upload.py +16 -14
  54. cosmotech/csm_data/commands/store/dump_to_azure.py +3 -2
  55. cosmotech/csm_data/commands/store/dump_to_postgresql.py +3 -2
  56. cosmotech/csm_data/commands/store/dump_to_s3.py +18 -16
  57. cosmotech/csm_data/commands/store/list_tables.py +3 -2
  58. cosmotech/csm_data/commands/store/load_csv_folder.py +10 -4
  59. cosmotech/csm_data/commands/store/load_from_singlestore.py +3 -2
  60. cosmotech/csm_data/commands/store/output.py +35 -0
  61. cosmotech/csm_data/commands/store/reset.py +8 -3
  62. cosmotech/csm_data/commands/store/store.py +3 -3
  63. cosmotech/csm_data/main.py +4 -4
  64. cosmotech/csm_data/utils/decorators.py +4 -3
  65. cosmotech/translation/coal/en-US/coal/cosmotech_api/initialization.yml +8 -0
  66. cosmotech/translation/coal/en-US/coal/services/dataset.yml +10 -14
  67. cosmotech/translation/coal/en-US/coal/store/output/data_interface.yml +1 -0
  68. cosmotech/translation/coal/en-US/coal/store/output/split.yml +6 -0
  69. cosmotech/translation/coal/en-US/coal/utils/configuration.yml +2 -0
  70. cosmotech/translation/csm_data/en-US/csm_data/commands/store/output.yml +7 -0
  71. {cosmotech_acceleration_library-1.1.0.dist-info → cosmotech_acceleration_library-2.1.0rc1.dist-info}/METADATA +29 -33
  72. cosmotech_acceleration_library-2.1.0rc1.dist-info/RECORD +153 -0
  73. {cosmotech_acceleration_library-1.1.0.dist-info → cosmotech_acceleration_library-2.1.0rc1.dist-info}/WHEEL +1 -1
  74. cosmotech/coal/azure/functions.py +0 -72
  75. cosmotech/coal/cosmotech_api/connection.py +0 -96
  76. cosmotech/coal/cosmotech_api/dataset/__init__.py +0 -26
  77. cosmotech/coal/cosmotech_api/dataset/converters.py +0 -164
  78. cosmotech/coal/cosmotech_api/dataset/download/__init__.py +0 -19
  79. cosmotech/coal/cosmotech_api/dataset/download/adt.py +0 -119
  80. cosmotech/coal/cosmotech_api/dataset/download/common.py +0 -140
  81. cosmotech/coal/cosmotech_api/dataset/download/file.py +0 -229
  82. cosmotech/coal/cosmotech_api/dataset/download/twingraph.py +0 -185
  83. cosmotech/coal/cosmotech_api/dataset/upload.py +0 -41
  84. cosmotech/coal/cosmotech_api/dataset/utils.py +0 -132
  85. cosmotech/coal/cosmotech_api/parameters.py +0 -48
  86. cosmotech/coal/cosmotech_api/run.py +0 -25
  87. cosmotech/coal/cosmotech_api/run_data.py +0 -173
  88. cosmotech/coal/cosmotech_api/run_template.py +0 -108
  89. cosmotech/coal/cosmotech_api/runner/__init__.py +0 -28
  90. cosmotech/coal/cosmotech_api/runner/data.py +0 -38
  91. cosmotech/coal/cosmotech_api/runner/datasets.py +0 -416
  92. cosmotech/coal/cosmotech_api/runner/download.py +0 -135
  93. cosmotech/coal/cosmotech_api/runner/metadata.py +0 -42
  94. cosmotech/coal/cosmotech_api/runner/parameters.py +0 -157
  95. cosmotech/coal/cosmotech_api/twin_data_layer.py +0 -512
  96. cosmotech/coal/cosmotech_api/workspace.py +0 -127
  97. cosmotech/coal/utils/postgresql.py +0 -236
  98. cosmotech/coal/utils/semver.py +0 -6
  99. cosmotech/csm_data/commands/api/rds_load_csv.py +0 -90
  100. cosmotech/csm_data/commands/api/rds_send_csv.py +0 -74
  101. cosmotech/csm_data/commands/api/rds_send_store.py +0 -74
  102. cosmotech/csm_data/commands/api/runtemplate_load_handler.py +0 -66
  103. cosmotech/csm_data/commands/api/tdl_load_files.py +0 -76
  104. cosmotech/csm_data/commands/api/tdl_send_files.py +0 -82
  105. cosmotech/orchestrator_plugins/csm-data/templates/api/rds_load_csv.json +0 -27
  106. cosmotech/orchestrator_plugins/csm-data/templates/api/rds_send_csv.json +0 -27
  107. cosmotech/orchestrator_plugins/csm-data/templates/api/rds_send_store.json +0 -27
  108. cosmotech/orchestrator_plugins/csm-data/templates/api/runtemplate_load_handler.json +0 -27
  109. cosmotech/orchestrator_plugins/csm-data/templates/api/tdl_load_files.json +0 -32
  110. cosmotech/orchestrator_plugins/csm-data/templates/api/tdl_send_files.json +0 -27
  111. cosmotech/translation/coal/en-US/coal/cosmotech_api/run_data.yml +0 -2
  112. cosmotech/translation/csm_data/en-US/csm_data/commands/api/rds_load_csv.yml +0 -13
  113. cosmotech/translation/csm_data/en-US/csm_data/commands/api/rds_send_csv.yml +0 -12
  114. cosmotech/translation/csm_data/en-US/csm_data/commands/api/rds_send_store.yml +0 -12
  115. cosmotech/translation/csm_data/en-US/csm_data/commands/api/tdl_load_files.yml +0 -14
  116. cosmotech/translation/csm_data/en-US/csm_data/commands/api/tdl_send_files.yml +0 -18
  117. cosmotech_acceleration_library-1.1.0.dist-info/RECORD +0 -171
  118. {cosmotech_acceleration_library-1.1.0.dist-info → cosmotech_acceleration_library-2.1.0rc1.dist-info}/entry_points.txt +0 -0
  119. {cosmotech_acceleration_library-1.1.0.dist-info → cosmotech_acceleration_library-2.1.0rc1.dist-info}/licenses/LICENSE +0 -0
  120. {cosmotech_acceleration_library-1.1.0.dist-info → cosmotech_acceleration_library-2.1.0rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,75 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+ from typing import Any, Optional
8
+
9
+ from cosmotech.orchestrator.utils.translate import T
10
+ from cosmotech_api import RunnerApi as BaseRunnerApi
11
+
12
+ from cosmotech.coal.cosmotech_api.apis.dataset import DatasetApi
13
+ from cosmotech.coal.cosmotech_api.objects.connection import Connection
14
+ from cosmotech.coal.cosmotech_api.objects.parameters import Parameters
15
+ from cosmotech.coal.utils.configuration import ENVIRONMENT_CONFIGURATION, Configuration
16
+ from cosmotech.coal.utils.logger import LOGGER
17
+
18
+
19
+ class RunnerApi(BaseRunnerApi, Connection):
20
+
21
+ def __init__(
22
+ self,
23
+ configuration: Configuration = ENVIRONMENT_CONFIGURATION,
24
+ ):
25
+ Connection.__init__(self, configuration)
26
+ BaseRunnerApi.__init__(self, self.api_client)
27
+
28
+ LOGGER.debug(T("coal.cosmotech_api.initialization.runner_api_initialized"))
29
+
30
+ def get_runner_metadata(
31
+ self,
32
+ runner_id: Optional[str] = None,
33
+ include: Optional[list[str]] = None,
34
+ exclude: Optional[list[str]] = None,
35
+ ) -> dict[str, Any]:
36
+ runner = self.get_runner(
37
+ self.configuration.cosmotech.organization_id,
38
+ self.configuration.cosmotech.workspace_id,
39
+ runner_id or self.configuration.cosmotech.runner_id,
40
+ )
41
+
42
+ return runner.model_dump(by_alias=True, exclude_none=True, include=include, exclude=exclude, mode="json")
43
+
44
+ def download_runner_data(
45
+ self,
46
+ download_datasets: Optional[str] = None,
47
+ ):
48
+ LOGGER.info(T("coal.cosmotech_api.runner.starting_download"))
49
+
50
+ # Get runner data
51
+ runner = self.get_runner(
52
+ self.configuration.cosmotech.organization_id,
53
+ self.configuration.cosmotech.workspace_id,
54
+ self.configuration.cosmotech.runner_id,
55
+ )
56
+
57
+ # Skip if no parameters found
58
+ if not runner.parameters_values:
59
+ LOGGER.warning(T("coal.cosmotech_api.runner.no_parameters"))
60
+ else:
61
+ LOGGER.info(T("coal.cosmotech_api.runner.loaded_data"))
62
+ parameters = Parameters(runner)
63
+ parameters.write_parameters_to_json(self.configuration.cosmotech.parameters_absolute_path)
64
+
65
+ if runner.datasets.parameter:
66
+ ds_api = DatasetApi(self.configuration)
67
+ ds_api.download_parameter(runner.datasets.parameter)
68
+
69
+ # Download datasets if requested
70
+ if download_datasets:
71
+ LOGGER.info(T("coal.cosmotech_api.runner.downloading_datasets").format(count=len(runner.datasets.bases)))
72
+ if runner.datasets.bases:
73
+ ds_api = DatasetApi(self.configuration)
74
+ for dataset_id in runner.datasets.bases:
75
+ ds_api.download_dataset(dataset_id)
@@ -0,0 +1,23 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+ from cosmotech.orchestrator.utils.translate import T
8
+ from cosmotech_api import SolutionApi as BaseSolutionApi
9
+
10
+ from cosmotech.coal.cosmotech_api.objects.connection import Connection
11
+ from cosmotech.coal.utils.configuration import ENVIRONMENT_CONFIGURATION, Configuration
12
+ from cosmotech.coal.utils.logger import LOGGER
13
+
14
+
15
+ class SolutionApi(BaseSolutionApi, Connection):
16
+ def __init__(
17
+ self,
18
+ configuration: Configuration = ENVIRONMENT_CONFIGURATION,
19
+ ):
20
+ Connection.__init__(self, configuration)
21
+ BaseSolutionApi.__init__(self, self.api_client)
22
+
23
+ LOGGER.debug(T("coal.cosmotech_api.initialization.solution_api_initialized"))
@@ -0,0 +1,108 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+ from pathlib import Path
8
+
9
+ from cosmotech.orchestrator.utils.translate import T
10
+ from cosmotech_api import ApiException
11
+ from cosmotech_api import WorkspaceApi as BaseWorkspaceApi
12
+
13
+ from cosmotech.coal.cosmotech_api.objects.connection import Connection
14
+ from cosmotech.coal.utils.configuration import ENVIRONMENT_CONFIGURATION, Configuration
15
+ from cosmotech.coal.utils.logger import LOGGER
16
+
17
+
18
+ class WorkspaceApi(BaseWorkspaceApi, Connection):
19
+
20
+ def __init__(
21
+ self,
22
+ configuration: Configuration = ENVIRONMENT_CONFIGURATION,
23
+ ):
24
+ Connection.__init__(self, configuration)
25
+ BaseWorkspaceApi.__init__(self, self.api_client)
26
+
27
+ LOGGER.debug(T("coal.cosmotech_api.initialization.workspace_api_initialized"))
28
+
29
+ def list_filtered_workspace_files(
30
+ self,
31
+ organization_id: str,
32
+ workspace_id: str,
33
+ file_prefix: str,
34
+ ) -> list[str]:
35
+ target_list = []
36
+ LOGGER.info(T("coal.cosmotech_api.workspace.target_is_folder"))
37
+ wsf = self.list_workspace_files(organization_id, workspace_id)
38
+ for workspace_file in wsf:
39
+ if workspace_file.file_name.startswith(file_prefix):
40
+ target_list.append(workspace_file.file_name)
41
+
42
+ if not target_list:
43
+ LOGGER.error(
44
+ T("coal.common.errors.data_no_workspace_files").format(
45
+ file_prefix=file_prefix, workspace_id=workspace_id
46
+ )
47
+ )
48
+ raise ValueError(
49
+ T("coal.common.errors.data_no_workspace_files").format(
50
+ file_prefix=file_prefix, workspace_id=workspace_id
51
+ )
52
+ )
53
+
54
+ return target_list
55
+
56
+ def download_workspace_file(
57
+ self,
58
+ organization_id: str,
59
+ workspace_id: str,
60
+ file_name: str,
61
+ target_dir: Path,
62
+ ) -> Path:
63
+ if target_dir.is_file():
64
+ raise ValueError(T("coal.common.file_operations.not_directory").format(target_dir=target_dir))
65
+
66
+ LOGGER.info(T("coal.cosmotech_api.workspace.loading_file").format(file_name=file_name))
67
+
68
+ _file_content = self.get_workspace_file(organization_id, workspace_id, file_name)
69
+
70
+ local_target_file = target_dir / file_name
71
+ local_target_file.parent.mkdir(parents=True, exist_ok=True)
72
+
73
+ with open(local_target_file, "wb") as _file:
74
+ _file.write(_file_content)
75
+
76
+ LOGGER.info(T("coal.cosmotech_api.workspace.file_loaded").format(file=local_target_file))
77
+
78
+ return local_target_file
79
+
80
+ def upload_workspace_file(
81
+ self,
82
+ organization_id: str,
83
+ workspace_id: str,
84
+ file_path: str,
85
+ workspace_path: str,
86
+ overwrite: bool = True,
87
+ ) -> str:
88
+ target_file = Path(file_path)
89
+ if not target_file.exists():
90
+ LOGGER.error(T("coal.common.file_operations.not_exists").format(file_path=file_path))
91
+ raise ValueError(T("coal.common.file_operations.not_exists").format(file_path=file_path))
92
+ if not target_file.is_file():
93
+ LOGGER.error(T("coal.common.file_operations.not_single_file").format(file_path=file_path))
94
+ raise ValueError(T("coal.common.file_operations.not_single_file").format(file_path=file_path))
95
+
96
+ destination = workspace_path + target_file.name if workspace_path.endswith("/") else workspace_path
97
+
98
+ LOGGER.info(T("coal.cosmotech_api.workspace.sending_to_api").format(destination=destination))
99
+ try:
100
+ _file = self.create_workspace_file(
101
+ organization_id, workspace_id, file_path, overwrite, destination=destination
102
+ )
103
+ except ApiException as e:
104
+ LOGGER.error(T("coal.common.file_operations.already_exists").format(csv_path=destination))
105
+ raise e
106
+
107
+ LOGGER.info(T("coal.cosmotech_api.workspace.file_sent").format(file=_file.file_name))
108
+ return _file.file_name
@@ -0,0 +1,9 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+
8
+ from cosmotech.coal.cosmotech_api.objects.connection import Connection
9
+ from cosmotech.coal.cosmotech_api.objects.parameters import Parameters
@@ -0,0 +1,125 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+
8
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
9
+ # This document and all information contained herein is the exclusive property -
10
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
11
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
12
+ # etc., to any person is prohibited unless it has been previously and
13
+ # specifically authorized by written means by Cosmo Tech.
14
+ import os
15
+ import pathlib
16
+
17
+ import cosmotech_api
18
+ from cosmotech.orchestrator.utils.translate import T
19
+ from cosmotech_api import ApiClient
20
+
21
+ from cosmotech.coal.utils.configuration import ENVIRONMENT_CONFIGURATION, Configuration
22
+ from cosmotech.coal.utils.logger import LOGGER
23
+
24
+
25
+ class Connection:
26
+ configuration: Configuration
27
+ api_client: ApiClient
28
+ api_type: str
29
+
30
+ # TODO :
31
+ # Replace use of environment variable by configuration usage
32
+ __api_env_keys = {"CSM_API_KEY", "CSM_API_URL"}
33
+ __azure_env_keys = {
34
+ "AZURE_CLIENT_ID",
35
+ "AZURE_CLIENT_SECRET",
36
+ "AZURE_TENANT_ID",
37
+ "CSM_API_URL",
38
+ "CSM_API_SCOPE",
39
+ }
40
+ __keycloak_env_keys = {
41
+ "IDP_TENANT_ID",
42
+ "IDP_CLIENT_ID",
43
+ "IDP_CLIENT_SECRET",
44
+ "IDP_BASE_URL",
45
+ "CSM_API_URL",
46
+ }
47
+
48
+ def __init__(
49
+ self,
50
+ configuration: Configuration = ENVIRONMENT_CONFIGURATION,
51
+ ):
52
+ self.configuration = configuration
53
+ self.api_client, self.api_type = self.get_api_client()
54
+
55
+ def get_api_client(self) -> (cosmotech_api.ApiClient, str):
56
+ existing_keys = set(os.environ.keys())
57
+ missing_azure_keys = self.__azure_env_keys - existing_keys
58
+ missing_api_keys = self.__api_env_keys - existing_keys
59
+ missing_keycloak_keys = self.__keycloak_env_keys - existing_keys
60
+
61
+ if all((missing_api_keys, missing_azure_keys, missing_keycloak_keys)):
62
+ LOGGER.error(T("coal.common.errors.no_env_vars"))
63
+ LOGGER.error(T("coal.cosmotech_api.connection.existing_sets"))
64
+ LOGGER.error(
65
+ T("coal.cosmotech_api.connection.azure_connection").format(keys=", ".join(self.__azure_env_keys))
66
+ )
67
+ LOGGER.error(
68
+ T("coal.cosmotech_api.connection.api_key_connection").format(keys=", ".join(self.__api_env_keys))
69
+ )
70
+ LOGGER.error(
71
+ T("coal.cosmotech_api.connection.keycloak_connection").format(keys=", ".join(self.__keycloak_env_keys))
72
+ )
73
+ raise EnvironmentError(T("coal.common.errors.no_env_vars"))
74
+
75
+ if not missing_keycloak_keys:
76
+ LOGGER.debug(T("coal.cosmotech_api.connection.found_keycloak"))
77
+ from keycloak import KeycloakOpenID
78
+
79
+ server_url = os.environ.get("IDP_BASE_URL")
80
+ if server_url[-1] != "/":
81
+ server_url = server_url + "/"
82
+ keycloack_parameters = dict(
83
+ server_url=server_url,
84
+ client_id=os.environ.get("IDP_CLIENT_ID"),
85
+ realm_name=os.environ.get("IDP_TENANT_ID"),
86
+ client_secret_key=os.environ.get("IDP_CLIENT_SECRET"),
87
+ )
88
+ if (ca_cert_path := os.environ.get("IDP_CA_CERT")) and pathlib.Path(ca_cert_path).exists():
89
+ LOGGER.info(T("coal.cosmotech_api.connection.found_cert_authority"))
90
+ keycloack_parameters["verify"] = ca_cert_path
91
+ keycloak_openid = KeycloakOpenID(**keycloack_parameters)
92
+
93
+ access_token = keycloak_openid.token(grant_type="client_credentials")
94
+
95
+ configuration = cosmotech_api.Configuration(
96
+ host=os.environ.get("CSM_API_URL"),
97
+ access_token=access_token["access_token"],
98
+ )
99
+ return cosmotech_api.ApiClient(configuration), "Keycloak Connection"
100
+
101
+ if not missing_api_keys:
102
+ LOGGER.debug(T("coal.cosmotech_api.connection.found_api_key"))
103
+ configuration = cosmotech_api.Configuration(
104
+ host=os.environ.get("CSM_API_URL"),
105
+ )
106
+ return (
107
+ cosmotech_api.ApiClient(
108
+ configuration,
109
+ os.environ.get("CSM_API_KEY_HEADER", "X-CSM-API-KEY"),
110
+ os.environ.get("CSM_API_KEY"),
111
+ ),
112
+ "Cosmo Tech API Key",
113
+ )
114
+
115
+ if not missing_azure_keys:
116
+ LOGGER.debug(T("coal.cosmotech_api.connection.found_azure"))
117
+ from azure.identity import EnvironmentCredential
118
+
119
+ credentials = EnvironmentCredential()
120
+ token = credentials.get_token(os.environ.get("CSM_API_SCOPE"))
121
+
122
+ configuration = cosmotech_api.Configuration(host=os.environ.get("CSM_API_URL"), access_token=token.token)
123
+ return cosmotech_api.ApiClient(configuration), "Azure Entra Connection"
124
+
125
+ raise EnvironmentError(T("coal.common.errors.no_valid_connection"))
@@ -0,0 +1,127 @@
1
+ # Copyright (C) - 2023 - 2025 - Cosmo Tech
2
+ # This document and all information contained herein is the exclusive property -
3
+ # including all intellectual property rights pertaining thereto - of Cosmo Tech.
4
+ # Any use, reproduction, translation, broadcasting, transmission, distribution,
5
+ # etc., to any person is prohibited unless it has been previously and
6
+ # specifically authorized by written means by Cosmo Tech.
7
+ import json
8
+ import os
9
+ import pathlib
10
+ from csv import DictWriter
11
+ from typing import Any, Dict, List
12
+
13
+ from cosmotech.orchestrator.utils.translate import T
14
+
15
+ from cosmotech.coal.utils.logger import LOGGER
16
+
17
+
18
+ class Parameters:
19
+ values: Dict[str, Any] = dict()
20
+ parameters_list: List[Dict[str, Any]] = list()
21
+
22
+ def __init__(self, runner_data: Any):
23
+ """
24
+ Extract parameters from runner data.
25
+
26
+ Args:
27
+ runner_data: Runner data object
28
+
29
+ Returns:
30
+ Dictionary mapping parameter IDs to values
31
+ """
32
+ for parameter in runner_data.parameters_values:
33
+ self.values[parameter.parameter_id] = parameter.value
34
+ self.parameters_list = self.format_parameters_list(runner_data)
35
+
36
+ @staticmethod
37
+ def format_parameters_list(runner_data: Any) -> List[Dict[str, Any]]:
38
+ """
39
+ Format parameters from runner data as a list of dictionaries.
40
+
41
+ Args:
42
+ runner_data: Runner data object
43
+
44
+ Returns:
45
+ List of parameter dictionaries
46
+ """
47
+ parameters = []
48
+
49
+ if not runner_data.parameters_values:
50
+ return parameters
51
+
52
+ max_name_size = max(map(lambda r: len(r.parameter_id), runner_data.parameters_values))
53
+ max_type_size = max(map(lambda r: len(r.var_type), runner_data.parameters_values))
54
+
55
+ for parameter_data in runner_data.parameters_values:
56
+ parameter_name = parameter_data.parameter_id
57
+ value = parameter_data.value
58
+ var_type = parameter_data.var_type
59
+ is_inherited = parameter_data.is_inherited
60
+
61
+ parameters.append(
62
+ {
63
+ "parameterId": parameter_name,
64
+ "value": value,
65
+ "varType": var_type,
66
+ "isInherited": is_inherited,
67
+ }
68
+ )
69
+
70
+ LOGGER.debug(
71
+ T("coal.cosmotech_api.runner.parameter_debug").format(
72
+ param_id=parameter_name,
73
+ max_name_size=max_name_size,
74
+ var_type=var_type,
75
+ max_type_size=max_type_size,
76
+ value=value,
77
+ inherited=" inherited" if is_inherited else "",
78
+ )
79
+ )
80
+
81
+ return parameters
82
+
83
+ def write_parameters_to_json(
84
+ self,
85
+ parameter_folder: str,
86
+ ) -> str:
87
+ pathlib.Path(parameter_folder).mkdir(exist_ok=True, parents=True)
88
+ tmp_parameter_file = os.path.join(parameter_folder, "parameters.json")
89
+
90
+ LOGGER.info(T("coal.cosmotech_api.runner.generating_file").format(file=tmp_parameter_file))
91
+
92
+ with open(tmp_parameter_file, "w") as _file:
93
+ json.dump(self.parameters_list, _file, indent=2)
94
+
95
+ return tmp_parameter_file
96
+
97
+ def write_parameters_to_csv(
98
+ self,
99
+ parameter_folder: str,
100
+ ) -> str:
101
+ pathlib.Path(parameter_folder).mkdir(exist_ok=True, parents=True)
102
+ tmp_parameter_file = os.path.join(parameter_folder, "parameters.csv")
103
+
104
+ LOGGER.info(T("coal.cosmotech_api.runner.generating_file").format(file=tmp_parameter_file))
105
+
106
+ with open(tmp_parameter_file, "w") as _file:
107
+ _w = DictWriter(_file, fieldnames=["parameterId", "value", "varType", "isInherited"])
108
+ _w.writeheader()
109
+ _w.writerows(self.parameters_list)
110
+
111
+ return tmp_parameter_file
112
+
113
+ def write_parameters(
114
+ self,
115
+ parameter_folder: str,
116
+ write_csv: bool = True,
117
+ write_json: bool = False,
118
+ ) -> Dict[str, str]:
119
+ result = {}
120
+
121
+ if write_csv:
122
+ result["csv"] = self.write_parameters_to_csv(parameter_folder)
123
+
124
+ if write_json:
125
+ result["json"] = self.write_parameters_to_json(parameter_folder)
126
+
127
+ return result
@@ -13,83 +13,100 @@ for runner metadata operations.
13
13
  """
14
14
 
15
15
  from adbc_driver_postgresql import dbapi
16
+ from cosmotech.orchestrator.utils.translate import T
16
17
 
17
- from cosmotech.coal.cosmotech_api.connection import get_api_client
18
- from cosmotech.coal.cosmotech_api.runner.metadata import get_runner_metadata
18
+ from cosmotech.coal.cosmotech_api.apis.runner import RunnerApi
19
+ from cosmotech.coal.postgresql.utils import PostgresUtils
20
+ from cosmotech.coal.utils.configuration import Configuration
19
21
  from cosmotech.coal.utils.logger import LOGGER
20
- from cosmotech.coal.utils.postgresql import generate_postgresql_full_uri
21
- from cosmotech.orchestrator.utils.translate import T
22
22
 
23
23
 
24
24
  def send_runner_metadata_to_postgresql(
25
- organization_id: str,
26
- workspace_id: str,
27
- runner_id: str,
28
- postgres_host: str,
29
- postgres_port: int,
30
- postgres_db: str,
31
- postgres_schema: str,
32
- postgres_user: str,
33
- postgres_password: str,
34
- table_prefix: str = "Cosmotech_",
35
- force_encode: bool = False,
36
- ) -> None:
25
+ configuration: Configuration,
26
+ ) -> str:
37
27
  """
38
28
  Send runner metadata to a PostgreSQL database.
39
29
 
40
30
  Args:
31
+ configuration: coal configuration
41
32
  organization_id: Organization ID
42
33
  workspace_id: Workspace ID
43
34
  runner_id: Runner ID
44
- postgres_host: PostgreSQL host
45
- postgres_port: PostgreSQL port
46
- postgres_db: PostgreSQL database name
47
- postgres_schema: PostgreSQL schema
48
- postgres_user: PostgreSQL username
49
- postgres_password: PostgreSQL password
50
- table_prefix: Table prefix
51
- force_encode: force password encoding to percent encoding
52
35
  """
53
- # Get runner metadata
54
- with get_api_client()[0] as api_client:
55
- runner = get_runner_metadata(api_client, organization_id, workspace_id, runner_id)
36
+ _psql = PostgresUtils(configuration)
56
37
 
57
- # Generate PostgreSQL URI
58
- postgresql_full_uri = generate_postgresql_full_uri(
59
- postgres_host, str(postgres_port), postgres_db, postgres_user, postgres_password, force_encode=force_encode
38
+ # Get runner metadata
39
+ _runner_api = RunnerApi(configuration)
40
+ runner = _runner_api.get_runner_metadata(
41
+ configuration.cosmotech.runner_id,
60
42
  )
61
43
 
62
44
  # Connect to PostgreSQL and update runner metadata
63
- with dbapi.connect(postgresql_full_uri, autocommit=True) as conn:
45
+ with dbapi.connect(_psql.full_uri, autocommit=True) as conn:
64
46
  with conn.cursor() as curs:
65
- schema_table = f"{postgres_schema}.{table_prefix}RunnerMetadata"
47
+ schema_table = f"{str(_psql.db_schema)}.{str(_psql.table_prefix)}RunnerMetadata"
66
48
  sql_create_table = f"""
67
49
  CREATE TABLE IF NOT EXISTS {schema_table} (
68
50
  id varchar(32) PRIMARY KEY,
69
51
  name varchar(256),
70
- last_run_id varchar(32),
52
+ last_csm_run_id varchar(32),
71
53
  run_template_id varchar(32)
72
54
  );
73
55
  """
74
- sql_upsert = f"""
75
- INSERT INTO {schema_table} (id, name, last_run_id, run_template_id)
76
- VALUES(%s, %s, %s, %s)
77
- ON CONFLICT (id)
78
- DO
79
- UPDATE SET name = EXCLUDED.name, last_run_id = EXCLUDED.last_run_id;
80
- """
81
56
  LOGGER.info(T("coal.services.postgresql.creating_table").format(schema_table=schema_table))
82
57
  curs.execute(sql_create_table)
83
58
  conn.commit()
84
59
  LOGGER.info(T("coal.services.postgresql.metadata"))
60
+ sql_upsert = f"""
61
+ INSERT INTO {schema_table} (id, name, last_csm_run_id, run_template_id)
62
+ VALUES ($1, $2, $3, $4)
63
+ ON CONFLICT (id)
64
+ DO
65
+ UPDATE SET name = EXCLUDED.name, last_csm_run_id = EXCLUDED.last_csm_run_id;
66
+ """
67
+ LOGGER.debug(runner)
85
68
  curs.execute(
86
69
  sql_upsert,
87
70
  (
88
71
  runner.get("id"),
89
72
  runner.get("name"),
90
- runner.get("lastRunId"),
73
+ runner.get("lastRunInfo").get("lastRunId"),
91
74
  runner.get("runTemplateId"),
92
75
  ),
93
76
  )
94
77
  conn.commit()
95
78
  LOGGER.info(T("coal.services.postgresql.metadata_updated"))
79
+ return runner.get("lastRunInfo").get("lastRunId")
80
+
81
+
82
+ def remove_runner_metadata_from_postgresql(
83
+ configuration: Configuration,
84
+ ) -> str:
85
+ """
86
+ Removes run_id from metadata table that trigger cascade delete on other tables
87
+
88
+ Args:
89
+ configuration: coal configuration
90
+ organization_id: Organization ID
91
+ workspace_id: Workspace ID
92
+ runner_id: Runner ID
93
+ """
94
+ _psql = PostgresUtils(configuration)
95
+
96
+ # Get runner metadata
97
+ _runner_api = RunnerApi(configuration)
98
+ runner = _runner_api.get_runner_metadata(
99
+ configuration.cosmotech.runner_id,
100
+ )
101
+
102
+ # Connect to PostgreSQL and remove runner metadata row
103
+ with dbapi.connect(_psql.full_uri, autocommit=True) as conn:
104
+ with conn.cursor() as curs:
105
+ schema_table = f"{_psql.db_schema}.{_psql.table_prefix}RunnerMetadata"
106
+ sql_delete_from_metatable = f"""
107
+ DELETE FROM {schema_table}
108
+ WHERE last_csm_run_id={runner.get("lastRunId")};
109
+ """
110
+ curs.execute(sql_delete_from_metatable)
111
+ conn.commit()
112
+ return runner.get("lastRunId")