aplos-nca-saas-sdk 0.0.11__py3-none-any.whl → 0.0.13__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.
- aplos_nca_saas_sdk/integration_testing/configs/_config_base.py +7 -2
- aplos_nca_saas_sdk/integration_testing/configs/app_settings_config.py +88 -0
- aplos_nca_saas_sdk/integration_testing/configs/config_sample.json +31 -18
- aplos_nca_saas_sdk/integration_testing/configs/file_upload_config.py +94 -0
- aplos_nca_saas_sdk/integration_testing/configs/{login.py → login_config.py} +36 -23
- aplos_nca_saas_sdk/integration_testing/configs/nca_execution_config.py +186 -0
- aplos_nca_saas_sdk/integration_testing/files/executions/config1.json +46 -0
- aplos_nca_saas_sdk/integration_testing/integration_test_base.py +10 -3
- aplos_nca_saas_sdk/integration_testing/integration_test_configurations.py +15 -7
- aplos_nca_saas_sdk/integration_testing/integration_test_factory.py +1 -1
- aplos_nca_saas_sdk/integration_testing/integration_test_response.py +1 -1
- aplos_nca_saas_sdk/integration_testing/integration_test_suite.py +10 -3
- aplos_nca_saas_sdk/integration_testing/main.py +6 -6
- aplos_nca_saas_sdk/integration_testing/readme.md +1 -1
- aplos_nca_saas_sdk/integration_testing/tests/app_configuration_test.py +3 -3
- aplos_nca_saas_sdk/integration_testing/tests/app_login_test.py +4 -4
- aplos_nca_saas_sdk/integration_testing/tests/file_upload_test.py +44 -92
- aplos_nca_saas_sdk/integration_testing/tests/nca_analysis_test.py +89 -0
- aplos_nca_saas_sdk/integration_testing/tests/{app_execution_test.py → validation_test.py} +1 -1
- aplos_nca_saas_sdk/nca_resources/_api_base.py +44 -0
- aplos_nca_saas_sdk/{aws_resources → nca_resources}/aws_cognito.py +13 -7
- aplos_nca_saas_sdk/{aws_resources → nca_resources}/aws_s3_presigned_payload.py +3 -3
- aplos_nca_saas_sdk/{aws_resources → nca_resources}/aws_s3_presigned_upload.py +28 -22
- aplos_nca_saas_sdk/nca_resources/nca_analysis.py +304 -0
- aplos_nca_saas_sdk/nca_resources/nca_app_configuration.py +4 -4
- aplos_nca_saas_sdk/nca_resources/{nca_login.py → nca_authenticator.py} +26 -23
- aplos_nca_saas_sdk/nca_resources/nca_endpoints.py +56 -30
- aplos_nca_saas_sdk/nca_resources/nca_file_download.py +130 -0
- aplos_nca_saas_sdk/nca_resources/nca_file_upload.py +27 -29
- aplos_nca_saas_sdk/nca_resources/nca_validations.py +34 -0
- aplos_nca_saas_sdk/run_analysis_execution.py +148 -0
- aplos_nca_saas_sdk/utilities/commandline_args.py +32 -38
- aplos_nca_saas_sdk/utilities/environment_services.py +3 -2
- aplos_nca_saas_sdk/utilities/environment_vars.py +17 -4
- aplos_nca_saas_sdk/utilities/file_utility.py +33 -0
- aplos_nca_saas_sdk/utilities/http_utility.py +5 -14
- aplos_nca_saas_sdk/version.py +1 -1
- {aplos_nca_saas_sdk-0.0.11.dist-info → aplos_nca_saas_sdk-0.0.13.dist-info}/METADATA +1 -1
- aplos_nca_saas_sdk-0.0.13.dist-info/RECORD +51 -0
- {aplos_nca_saas_sdk-0.0.11.dist-info → aplos_nca_saas_sdk-0.0.13.dist-info}/licenses/LICENSE +1 -1
- aplos_nca_saas_sdk/integration_testing/configs/app_settings.py +0 -88
- aplos_nca_saas_sdk/integration_testing/configs/file_upload.py +0 -104
- aplos_nca_saas_sdk/integration_testing/tests/app_validation_test.py +0 -5
- aplos_nca_saas_sdk/nca_resources/nca_executions.py +0 -387
- aplos_nca_saas_sdk-0.0.11.dist-info/RECORD +0 -44
- /aplos_nca_saas_sdk/{files/analysis_files/single_ev/configuration_single_ev.json → sample_files/analysis_files/single_ev/config.json} +0 -0
- /aplos_nca_saas_sdk/{files/analysis_files/single_ev/single_ev.csv → sample_files/analysis_files/single_ev/input.csv} +0 -0
- /aplos_nca_saas_sdk/{files → sample_files}/analysis_files/single_ev/meta_data.json +0 -0
- {aplos_nca_saas_sdk-0.0.11.dist-info → aplos_nca_saas_sdk-0.0.13.dist-info}/WHEEL +0 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
aplos_nca_saas_sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
aplos_nca_saas_sdk/run_analysis_execution.py,sha256=cYzGdAHOriDI7UHNFwzeaR6sHhiwNysx3__eMze8hZc,4445
|
3
|
+
aplos_nca_saas_sdk/version.py,sha256=jlXzv0jZuOy0IB5ancPlhN-0-xre_EGv3QEfLys8N-c,172
|
4
|
+
aplos_nca_saas_sdk/integration_testing/integration_test_base.py,sha256=ci3oOfz_McdDKGSrqOHF7F_vrvgTWo7HzxydNdPSHLs,2787
|
5
|
+
aplos_nca_saas_sdk/integration_testing/integration_test_configurations.py,sha256=gHQE1l9l7qunlg0APfYE2JZzdhYHMz-RJiXIiQu6l2U,1634
|
6
|
+
aplos_nca_saas_sdk/integration_testing/integration_test_factory.py,sha256=HdcGQycsBNv-cLGXfL3yVpRjLaj-QkAv1ih0L8yVXA0,2203
|
7
|
+
aplos_nca_saas_sdk/integration_testing/integration_test_response.py,sha256=Ocn2q_kdgEJB-4Ol-V3dh8MEkf6CJdrIiQVURUeM62o,819
|
8
|
+
aplos_nca_saas_sdk/integration_testing/integration_test_suite.py,sha256=L1ZFp3tE9zUnaCyzfTfKTYkgR_ksrIOi5YK7qViwKes,4413
|
9
|
+
aplos_nca_saas_sdk/integration_testing/main.py,sha256=ACYtOZS-S0d1ZXZGzIMKxo1MPkK9CnsSzq7E5nMwhYE,2040
|
10
|
+
aplos_nca_saas_sdk/integration_testing/readme.md,sha256=USg_Z8C3hYgOGgmDsv7DNR2IxRV0Xg6NvOYrwTBqZRE,626
|
11
|
+
aplos_nca_saas_sdk/integration_testing/configs/_config_base.py,sha256=PyXCiEbvM5usN8sbtVSEL0g1iqGh9bdE31kPORMJBIs,480
|
12
|
+
aplos_nca_saas_sdk/integration_testing/configs/app_settings_config.py,sha256=wgRjf98enkob2mfAV0bzjmfhMbovFsuMiURKyW9n83w,2611
|
13
|
+
aplos_nca_saas_sdk/integration_testing/configs/config_sample.json,sha256=5n56K0mh33n5Po8uFbYE_ChIGbLuuxhVlhFwrbdrqNA,3568
|
14
|
+
aplos_nca_saas_sdk/integration_testing/configs/file_upload_config.py,sha256=kODsim7yQVnXthBtbWwmRGfZFVN-QIevhQjSHdep6_4,3066
|
15
|
+
aplos_nca_saas_sdk/integration_testing/configs/login_config.py,sha256=HqPjYBiagGSKaPFForO_eF0oKQqZzc3WEr4jEzh9kBc,3560
|
16
|
+
aplos_nca_saas_sdk/integration_testing/configs/nca_execution_config.py,sha256=gS9W6nJ4ypgGqWcRTpVHpiEVcKsTuyI6zmvL_U5Bqhs,5769
|
17
|
+
aplos_nca_saas_sdk/integration_testing/files/executions/config1.json,sha256=m6-wSzfrB-7w4CgIGpmT9y0_zevp-Q6So9eXU9HsVpQ,945
|
18
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.csv,sha256=9TGDiMkft7ltFFKk_8RyzuhuloIpe_fZs0Nw0PN3BkM,263
|
19
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.dat,sha256=9TGDiMkft7ltFFKk_8RyzuhuloIpe_fZs0Nw0PN3BkM,263
|
20
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.sas7bdat,sha256=bh4w5QPHwjgLEhk752BmUwztkcBmx8iAywZdiiWZuw0,16384
|
21
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xls,sha256=ymFgWvoUC--OxsXrj2JInjK3eSbR7jnGbpHEpiadNCI,26624
|
22
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xlsx,sha256=i8pOYfA-muNxrtCKfUfi4Hi3JvSx49ZgtfvIUQTzRJo,10481
|
23
|
+
aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xpt,sha256=vve5eYrs3nEuGGQgm710tzzYkhrn3cfcy3--CVe4IGs,1920
|
24
|
+
aplos_nca_saas_sdk/integration_testing/tests/app_configuration_test.py,sha256=rzGWSCJPah7C8htp1atsaWKtkyRdfyd3ku8UTYQ20mc,1569
|
25
|
+
aplos_nca_saas_sdk/integration_testing/tests/app_login_test.py,sha256=NLI4DUICspa_uZi-w2Q3XufDL0ERLVvn1DavNpYDK7I,1650
|
26
|
+
aplos_nca_saas_sdk/integration_testing/tests/file_upload_test.py,sha256=v3-5vEVPVREQDBColJeAGg2grG3c8A4WTxZnE3_ki9c,3936
|
27
|
+
aplos_nca_saas_sdk/integration_testing/tests/nca_analysis_test.py,sha256=RIxarfp_ggD4HgVb8xnMynIDZG2HZ0hmv6sZj1-T73c,3261
|
28
|
+
aplos_nca_saas_sdk/integration_testing/tests/validation_test.py,sha256=68BRZbjPa7p_n5CQeEzSP5hwPAZ921NP8vbmCaCn2pQ,150
|
29
|
+
aplos_nca_saas_sdk/nca_resources/_api_base.py,sha256=qxMSiHV4014L733ii4EJ2JUwQwKkuHi5Rm6cPEdS3cA,1278
|
30
|
+
aplos_nca_saas_sdk/nca_resources/aws_cognito.py,sha256=lc6GCvoTBx_Dmezoxt80xksiuxXjSwkynj-1Sg0vzwY,6576
|
31
|
+
aplos_nca_saas_sdk/nca_resources/aws_s3_presigned_payload.py,sha256=S9LvUEjzJqLYob-JmNXdIe0Uj__fVtcF4LDQB5538vk,2001
|
32
|
+
aplos_nca_saas_sdk/nca_resources/aws_s3_presigned_upload.py,sha256=ExZUjJ4Yyu-oQyVMNtyl7KqxFfr4hIwIN31RFxg6EfM,4476
|
33
|
+
aplos_nca_saas_sdk/nca_resources/nca_analysis.py,sha256=Fx-M7mkNKho0IbxLz7swSgexAlIXm-XeDxFchTOhHrI,10630
|
34
|
+
aplos_nca_saas_sdk/nca_resources/nca_app_configuration.py,sha256=VZNZi0_NV4QjNgBSM9csq5qedc6-qvzaXwXyLfymB6M,1845
|
35
|
+
aplos_nca_saas_sdk/nca_resources/nca_authenticator.py,sha256=3mXHs2zMsvo2zirudpgynC3Y9woFrfkmo3mysqfJyxk,3126
|
36
|
+
aplos_nca_saas_sdk/nca_resources/nca_endpoints.py,sha256=lYtktEG0yXleRoFLkab3fw7MaaaZIjdvPB2iZVlIg1s,2373
|
37
|
+
aplos_nca_saas_sdk/nca_resources/nca_file_download.py,sha256=_ryJta9f6CBOh0OsE6P4FMGZggjYWu_kj1ZuzVVuruc,4190
|
38
|
+
aplos_nca_saas_sdk/nca_resources/nca_file_upload.py,sha256=dqETMtnzYZilPlFfLKW7o3DrrBLObeIGgILWPyD-3ZI,1653
|
39
|
+
aplos_nca_saas_sdk/nca_resources/nca_validations.py,sha256=INv3bIHH3caOjByDknTqogDjqaYmHtZCDldc2AKpqOM,785
|
40
|
+
aplos_nca_saas_sdk/sample_files/analysis_files/single_ev/config.json,sha256=lLnRV0jwzaSn32D8NlOekOF5oGFfUwugUlvlwoKz540,986
|
41
|
+
aplos_nca_saas_sdk/sample_files/analysis_files/single_ev/input.csv,sha256=qFSAlgLOmERsabMmp1X6PAZa-8yFthZlHacM_f7_AOY,6528
|
42
|
+
aplos_nca_saas_sdk/sample_files/analysis_files/single_ev/meta_data.json,sha256=p1KYOAe5Cl3rjtfF1t96oRG-QtFJJCo9otReRPNtvIk,447
|
43
|
+
aplos_nca_saas_sdk/utilities/commandline_args.py,sha256=7HgB4AhQHHovqz_VOf6ndLubPhHQFHX5DhWz7Dx0OMo,10909
|
44
|
+
aplos_nca_saas_sdk/utilities/environment_services.py,sha256=eKlaz-3VAPHUe-MN0rc_AdJfzdiej86vhARE-ulWL5A,3375
|
45
|
+
aplos_nca_saas_sdk/utilities/environment_vars.py,sha256=aXheFXg6FVMaSYLe2LmoWRF5Ks9vwxDazO4XYb4vLjc,1132
|
46
|
+
aplos_nca_saas_sdk/utilities/file_utility.py,sha256=EUvQ65aXN6OdILniuiDQ2rPRA9sFmvUoAehifEjRgUY,1025
|
47
|
+
aplos_nca_saas_sdk/utilities/http_utility.py,sha256=DQ-ClLOmNoyPn5vhrSh4q-2wi4ViP_gplJD9asEKDM8,464
|
48
|
+
aplos_nca_saas_sdk-0.0.13.dist-info/METADATA,sha256=pZU_i1FxKlTBfAeTwcrBdN9ix9OA1tG4Jt4PeGb4ZRU,3792
|
49
|
+
aplos_nca_saas_sdk-0.0.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
50
|
+
aplos_nca_saas_sdk-0.0.13.dist-info/licenses/LICENSE,sha256=JQMpBrnqu_m2tISmyh6_dTgb8-m3HNnA51fuOh2TzkE,1076
|
51
|
+
aplos_nca_saas_sdk-0.0.13.dist-info/RECORD,,
|
@@ -1,88 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Copyright 2024 Aplos Analytics
|
3
|
-
All Rights Reserved. www.aplosanalytics.com LICENSED MATERIALS
|
4
|
-
Property of Aplos Analytics, Utah, USA
|
5
|
-
"""
|
6
|
-
|
7
|
-
from typing import List, Dict, Any
|
8
|
-
from aplos_nca_saas_sdk.integration_testing.configs._config_base import ConfigBase
|
9
|
-
|
10
|
-
|
11
|
-
class ApplicationDomain(ConfigBase):
|
12
|
-
"""
|
13
|
-
Application Domain: Defines the domains that the application configuration tests will check against
|
14
|
-
|
15
|
-
"""
|
16
|
-
|
17
|
-
def __init__(self, domain: str | None = None):
|
18
|
-
super().__init__()
|
19
|
-
self.__domain: str | None = domain
|
20
|
-
|
21
|
-
@property
|
22
|
-
def domain(self) -> str:
|
23
|
-
"""The domain to validate"""
|
24
|
-
if self.__domain is None:
|
25
|
-
raise RuntimeError("Domain is not set")
|
26
|
-
return self.__domain
|
27
|
-
|
28
|
-
@domain.setter
|
29
|
-
def domain(self, value: str):
|
30
|
-
self.__domain = value
|
31
|
-
|
32
|
-
|
33
|
-
class ApplicationDomains(ConfigBase):
|
34
|
-
"""
|
35
|
-
Application ApplicationDomain: Defines the Domains that the application configuration tests will check against
|
36
|
-
|
37
|
-
"""
|
38
|
-
|
39
|
-
def __init__(self):
|
40
|
-
super().__init__()
|
41
|
-
self.__domains: List[ApplicationDomain] = []
|
42
|
-
|
43
|
-
@property
|
44
|
-
def list(self) -> List[ApplicationDomain]:
|
45
|
-
"""List the logins"""
|
46
|
-
return self.__domains
|
47
|
-
|
48
|
-
def add(self, *, domain: str, enabled: bool = True):
|
49
|
-
"""Add a loging"""
|
50
|
-
app_domain = ApplicationDomain()
|
51
|
-
app_domain.domain = domain
|
52
|
-
app_domain.enabled = enabled
|
53
|
-
self.__domains.append(app_domain)
|
54
|
-
|
55
|
-
def load(self, test_config: Dict[str, Any]):
|
56
|
-
"""Load the logins from a list of dictionaries"""
|
57
|
-
# self.enabled = bool(test_config.get("enabled", True))
|
58
|
-
super().load(test_config)
|
59
|
-
domains: List[Dict[str, Any]] = test_config.get("domains", [])
|
60
|
-
|
61
|
-
domain: Dict[str, Any]
|
62
|
-
for domain in domains:
|
63
|
-
app_domain = ApplicationDomain()
|
64
|
-
app_domain.domain = domain.get("domain", None)
|
65
|
-
app_domain.enabled = bool(domain.get("enabled", True))
|
66
|
-
|
67
|
-
self.__domains.append(app_domain)
|
68
|
-
|
69
|
-
|
70
|
-
class ApplicationSettings(ConfigBase):
|
71
|
-
"""
|
72
|
-
Application Settings: Defines the domains that the application settings (configuration endpoint) tests will check against
|
73
|
-
|
74
|
-
"""
|
75
|
-
|
76
|
-
def __init__(self):
|
77
|
-
super().__init__()
|
78
|
-
self.__domains: ApplicationDomains = ApplicationDomains()
|
79
|
-
|
80
|
-
@property
|
81
|
-
def domains(self) -> ApplicationDomains:
|
82
|
-
"""List of the domain"""
|
83
|
-
return self.__domains
|
84
|
-
|
85
|
-
def load(self, test_config: Dict[str, Any]):
|
86
|
-
"""Load the domains from the config"""
|
87
|
-
super().load(test_config)
|
88
|
-
self.domains.load(test_config)
|
@@ -1,104 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Copyright 2024 Aplos Analytics
|
3
|
-
All Rights Reserved. www.aplosanalytics.com LICENSED MATERIALS
|
4
|
-
Property of Aplos Analytics, Utah, USA
|
5
|
-
"""
|
6
|
-
|
7
|
-
import os
|
8
|
-
from typing import List, Dict, Any
|
9
|
-
from aplos_nca_saas_sdk.integration_testing.configs._config_base import ConfigBase
|
10
|
-
from aplos_nca_saas_sdk.integration_testing.configs.login import Login
|
11
|
-
from aplos_nca_saas_sdk.utilities.environment_services import EnvironmentServices
|
12
|
-
|
13
|
-
|
14
|
-
class FileUpload(ConfigBase):
|
15
|
-
"""
|
16
|
-
File Upload: Defines the login that the application configuration tests will check against
|
17
|
-
|
18
|
-
"""
|
19
|
-
|
20
|
-
def __init__(self, login: Login, filepath: str):
|
21
|
-
super().__init__()
|
22
|
-
if login is None:
|
23
|
-
raise RuntimeError("Login is required")
|
24
|
-
self.__login = login
|
25
|
-
if filepath is None:
|
26
|
-
raise RuntimeError("Filepath is required")
|
27
|
-
self.__filepath = filepath
|
28
|
-
|
29
|
-
@property
|
30
|
-
def login(self) -> Login:
|
31
|
-
"""The users login"""
|
32
|
-
return self.__login
|
33
|
-
|
34
|
-
@property
|
35
|
-
def filepath(self) -> str:
|
36
|
-
"""The file path to file being uploaded"""
|
37
|
-
path = self.__load_filepath(self.__filepath)
|
38
|
-
|
39
|
-
if not os.path.exists(path):
|
40
|
-
raise RuntimeError(f"The Upload File was not found: {path}")
|
41
|
-
|
42
|
-
return path
|
43
|
-
|
44
|
-
def __load_filepath(self, filepath: str) -> str:
|
45
|
-
"""Load the filepath"""
|
46
|
-
if filepath is None:
|
47
|
-
raise RuntimeError("Filepath is required")
|
48
|
-
elif filepath.startswith("${module}"):
|
49
|
-
# find the path
|
50
|
-
es: EnvironmentServices = EnvironmentServices()
|
51
|
-
root = es.find_module_path()
|
52
|
-
filepath = filepath.replace("${module}", root)
|
53
|
-
|
54
|
-
# get the correct os path separator
|
55
|
-
filepath = os.path.normpath(filepath)
|
56
|
-
return filepath
|
57
|
-
|
58
|
-
|
59
|
-
class FileUploads(ConfigBase):
|
60
|
-
"""
|
61
|
-
File Uploads: Defines the files that the application file upload tests will check against
|
62
|
-
|
63
|
-
"""
|
64
|
-
|
65
|
-
def __init__(self):
|
66
|
-
super().__init__()
|
67
|
-
self.__fileuploads: List[FileUpload] = []
|
68
|
-
|
69
|
-
@property
|
70
|
-
def list(self) -> List[FileUpload]:
|
71
|
-
"""List the file uploads"""
|
72
|
-
return filter(lambda x: x.enabled, self.__fileuploads)
|
73
|
-
|
74
|
-
def add(self, *, filepath: str, login: Login, enabled: bool = True):
|
75
|
-
"""Add a file upload"""
|
76
|
-
fileupload = FileUpload(login, filepath)
|
77
|
-
fileupload.enabled = enabled
|
78
|
-
self.__fileuploads.append(fileupload)
|
79
|
-
|
80
|
-
def load(self, test_config: Dict[str, Any]):
|
81
|
-
"""Load the file uploads from a list of dictionaries"""
|
82
|
-
|
83
|
-
super().load(test_config)
|
84
|
-
test_config_login: Login = self.try_load_login(test_config.get("login", None))
|
85
|
-
fileuploads: List[Dict[str, Any]] = test_config.get("files", [])
|
86
|
-
login: Login = None
|
87
|
-
for fileupload in fileuploads:
|
88
|
-
enabled = bool(fileupload.get("enabled", True))
|
89
|
-
if "login" in fileupload:
|
90
|
-
login = self.try_load_login(fileupload["login"])
|
91
|
-
else:
|
92
|
-
login = test_config_login
|
93
|
-
|
94
|
-
self.add(filepath=fileupload["file"], login=login, enabled=enabled)
|
95
|
-
|
96
|
-
def try_load_login(self, login_config: Dict[str, Any]) -> Login | None:
|
97
|
-
"""Attempts to intialize a Login from a configuration object"""
|
98
|
-
login: Login = None
|
99
|
-
if login_config is not None:
|
100
|
-
username = login_config.get("username", None)
|
101
|
-
password = login_config.get("password", None)
|
102
|
-
domain = login_config.get("domain", None)
|
103
|
-
login = Login(username, password, domain)
|
104
|
-
return login
|
@@ -1,387 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Copyright 2024 Aplos Analytics
|
3
|
-
All Rights Reserved. www.aplosanalytics.com LICENSED MATERIALS
|
4
|
-
Property of Aplos Analytics, Utah, USA
|
5
|
-
"""
|
6
|
-
|
7
|
-
import json
|
8
|
-
import os
|
9
|
-
import time
|
10
|
-
import zipfile
|
11
|
-
from datetime import datetime, timedelta
|
12
|
-
from pathlib import Path
|
13
|
-
from typing import Any, Dict
|
14
|
-
|
15
|
-
import requests
|
16
|
-
|
17
|
-
from aplos_nca_saas_sdk.aws_resources.aws_cognito import CognitoAuthenication
|
18
|
-
from aplos_nca_saas_sdk.aws_resources.aws_s3_presigned_upload import (
|
19
|
-
S3PresignedUpload,
|
20
|
-
)
|
21
|
-
from aplos_nca_saas_sdk.utilities.commandline_args import CommandlineArgs
|
22
|
-
from aplos_nca_saas_sdk.utilities.http_utility import HttpUtilities, Routes
|
23
|
-
|
24
|
-
|
25
|
-
class NCAEngine:
|
26
|
-
"""NCA Engine Access"""
|
27
|
-
|
28
|
-
def __init__(
|
29
|
-
self, api_domain: str | None, cognito_client_id: str | None, region: str | None
|
30
|
-
) -> None:
|
31
|
-
self.jwt: str
|
32
|
-
self.access_token: str | None = None
|
33
|
-
self.refresh_token: str | None = None
|
34
|
-
self.__api_domain: str | None = api_domain
|
35
|
-
self.verbose: bool = False
|
36
|
-
|
37
|
-
self.cognito: CognitoAuthenication = CognitoAuthenication(
|
38
|
-
client_id=cognito_client_id, region=region
|
39
|
-
)
|
40
|
-
|
41
|
-
if not self.__api_domain:
|
42
|
-
raise RuntimeError(
|
43
|
-
"Missing Aplos Api Domain. "
|
44
|
-
"Pass in the api_domain as a command arg or set the APLOS_API_DOMAIN environment var."
|
45
|
-
)
|
46
|
-
|
47
|
-
@property
|
48
|
-
def api_root(self) -> str:
|
49
|
-
"""Gets the base url"""
|
50
|
-
if self.__api_domain is None:
|
51
|
-
raise RuntimeError("Missing Aplos Api Domain")
|
52
|
-
|
53
|
-
url = HttpUtilities.build_url(self.__api_domain)
|
54
|
-
if isinstance(url, str):
|
55
|
-
return (
|
56
|
-
f"{url}/tenants/{self.cognito.tenant_id}/users/{self.cognito.user_id}"
|
57
|
-
)
|
58
|
-
|
59
|
-
raise RuntimeError("Missing Aplos Api Domain")
|
60
|
-
|
61
|
-
def execute(
|
62
|
-
self,
|
63
|
-
username: str,
|
64
|
-
password: str,
|
65
|
-
input_file_path: str,
|
66
|
-
config_data: dict,
|
67
|
-
*,
|
68
|
-
meta_data: str | dict | None = None,
|
69
|
-
wait_for_results: bool = True,
|
70
|
-
output_directory: str | None = None,
|
71
|
-
unzip_after_download: bool = False,
|
72
|
-
) -> None:
|
73
|
-
"""_summary_
|
74
|
-
|
75
|
-
Args:
|
76
|
-
username (str): the username
|
77
|
-
password (str): the users password
|
78
|
-
input_file_path (str): the path to the input (anlysis) file
|
79
|
-
config_data (dict): analysis configuration infomration
|
80
|
-
meta_data (str | dict | None, optional): meta data attached to the execution. Defaults to None.
|
81
|
-
wait_for_results (bool, optional): should the program wait for results. Defaults to True.
|
82
|
-
output_directory (str, optional): the output directory. Defaults to None (the local directy is used)
|
83
|
-
unzip_after_download (bool): Results are downloaded as a zip file, this option will unzip them automatically. Defaults to False
|
84
|
-
"""
|
85
|
-
if self.verbose:
|
86
|
-
print("\tLogging in.")
|
87
|
-
self.jwt = self.cognito.login(username=username, password=password)
|
88
|
-
|
89
|
-
if self.verbose:
|
90
|
-
print("\tUploading the analysis file.")
|
91
|
-
uploader: S3PresignedUpload = S3PresignedUpload(self.jwt, str(self.api_root))
|
92
|
-
upload_response: Dict[str, Any] = uploader.upload_file(input_file_path)
|
93
|
-
|
94
|
-
if self.verbose:
|
95
|
-
print("\tStarting the execution.")
|
96
|
-
execution_id = self.run_analysis(
|
97
|
-
file_id=upload_response.get("file_id", ""),
|
98
|
-
config_data=config_data,
|
99
|
-
meta_data=meta_data,
|
100
|
-
)
|
101
|
-
|
102
|
-
if execution_id and wait_for_results:
|
103
|
-
# wait for it
|
104
|
-
download_url = self.wait_for_results(execution_id=execution_id)
|
105
|
-
# download the files
|
106
|
-
if download_url:
|
107
|
-
if self.verbose:
|
108
|
-
print("\tDownloading the results.")
|
109
|
-
self.download_file(
|
110
|
-
download_url,
|
111
|
-
output_directory=output_directory,
|
112
|
-
do_unzip=unzip_after_download,
|
113
|
-
)
|
114
|
-
|
115
|
-
def run_analysis(
|
116
|
-
self,
|
117
|
-
file_id: str,
|
118
|
-
config_data: dict,
|
119
|
-
meta_data: str | dict | None = None,
|
120
|
-
) -> str:
|
121
|
-
"""
|
122
|
-
Run the analysis
|
123
|
-
|
124
|
-
Args:
|
125
|
-
bucket_name (str): s3 bucket name for your organization. this is returned to you
|
126
|
-
object_key (str): 3s object key for the file you are running an analysis on.
|
127
|
-
config_data (dict): the config_data for the analysis file
|
128
|
-
meta_data (str | dict): Optional. Any meta data you'd like attached to this execution
|
129
|
-
Returns:
|
130
|
-
str: the execution id
|
131
|
-
"""
|
132
|
-
|
133
|
-
if not file_id:
|
134
|
-
raise ValueError("Missing file_id. Please provide a valid file_id.")
|
135
|
-
|
136
|
-
if not config_data:
|
137
|
-
raise ValueError(
|
138
|
-
"Missing config_data. Please provide a valid config_data."
|
139
|
-
)
|
140
|
-
headers = HttpUtilities.get_headers(self.jwt)
|
141
|
-
# to start a new execution we need the location of the file (s3 bucket and object key)
|
142
|
-
# you basic configuration
|
143
|
-
# optional meta data
|
144
|
-
|
145
|
-
submission = {
|
146
|
-
"file": {"id": file_id},
|
147
|
-
"configuration": config_data,
|
148
|
-
"meta_data": meta_data,
|
149
|
-
}
|
150
|
-
url = f"{str(self.api_root)}/{Routes.NCA_EXECUTIONS}"
|
151
|
-
response: requests.Response = requests.post(
|
152
|
-
url, headers=headers, data=json.dumps(submission), timeout=30
|
153
|
-
)
|
154
|
-
json_response: dict = response.json()
|
155
|
-
|
156
|
-
if response.status_code == 403:
|
157
|
-
raise PermissionError(
|
158
|
-
"Failed to execute. A 403 response occured. "
|
159
|
-
"This could a token issue or a url path issue "
|
160
|
-
"By default unknown gateway calls return 403 errors. "
|
161
|
-
)
|
162
|
-
elif response.status_code != 200:
|
163
|
-
raise RuntimeError(
|
164
|
-
f"Unknown Error occured during executions: {response.status_code}. "
|
165
|
-
f"Reason: {response.reason}"
|
166
|
-
)
|
167
|
-
|
168
|
-
execution_id = str(json_response.get("execution_id"))
|
169
|
-
if self.verbose:
|
170
|
-
print(f"\tExecution {execution_id} started.")
|
171
|
-
|
172
|
-
return execution_id
|
173
|
-
|
174
|
-
def wait_for_results(
|
175
|
-
self, execution_id: str, max_wait_in_minutes: int = 15
|
176
|
-
) -> str | None:
|
177
|
-
"""
|
178
|
-
Wait for results
|
179
|
-
Args:
|
180
|
-
execution_id (str): the analysis execution id
|
181
|
-
|
182
|
-
Returns:
|
183
|
-
str | None: on success: a url for download, on failure: None
|
184
|
-
"""
|
185
|
-
|
186
|
-
url = f"{self.api_root}/{Routes.NCA_EXECUTIONS}/{execution_id}"
|
187
|
-
|
188
|
-
headers = HttpUtilities.get_headers(self.jwt)
|
189
|
-
current_time = datetime.now()
|
190
|
-
# Create a timedelta object representing 15 minutes
|
191
|
-
time_delta = timedelta(minutes=max_wait_in_minutes)
|
192
|
-
|
193
|
-
# Add the timedelta to the current time
|
194
|
-
max_time = current_time + time_delta
|
195
|
-
|
196
|
-
complete = False
|
197
|
-
while not complete:
|
198
|
-
response = requests.get(url, headers=headers, timeout=30)
|
199
|
-
json_response: dict = response.json()
|
200
|
-
status = json_response.get("status")
|
201
|
-
complete = status == "complete"
|
202
|
-
elapsed = (
|
203
|
-
json_response.get("times", {}).get("elapsed", "0:00:00") or "--:--"
|
204
|
-
)
|
205
|
-
if status == "failed" or complete:
|
206
|
-
break
|
207
|
-
if not complete:
|
208
|
-
if self.verbose:
|
209
|
-
print(f"\t\twaiting for results.... {status}: {elapsed}")
|
210
|
-
time.sleep(5)
|
211
|
-
if datetime.now() > max_time:
|
212
|
-
status = "timeout"
|
213
|
-
break
|
214
|
-
if status is None and elapsed is None:
|
215
|
-
# we have a problem
|
216
|
-
status = "unknown issue"
|
217
|
-
break
|
218
|
-
|
219
|
-
if status == "complete":
|
220
|
-
if self.verbose:
|
221
|
-
print("\tExecution complete.")
|
222
|
-
print(f"\tExecution duration = {elapsed}.")
|
223
|
-
return json_response["presigned"]["url"]
|
224
|
-
else:
|
225
|
-
if self.verbose:
|
226
|
-
print(
|
227
|
-
f"\tExecution failed. Execution ID = {execution_id}. reason: {json_response.get('errors')}"
|
228
|
-
)
|
229
|
-
return None
|
230
|
-
|
231
|
-
def download_file(
|
232
|
-
self,
|
233
|
-
presigned_download_url: str,
|
234
|
-
output_directory: str | None = None,
|
235
|
-
do_unzip: bool = False,
|
236
|
-
) -> str | None:
|
237
|
-
"""
|
238
|
-
# Step 5
|
239
|
-
Download completed analysis files
|
240
|
-
|
241
|
-
Args:
|
242
|
-
presigned_download_url (str): presigned download url
|
243
|
-
output_directory (str | None): optional output directory
|
244
|
-
|
245
|
-
Returns:
|
246
|
-
str: file path to results or None
|
247
|
-
"""
|
248
|
-
if output_directory is None:
|
249
|
-
output_directory = str(Path(__file__).parent.parent)
|
250
|
-
output_directory = os.path.join(output_directory, ".aplos-nca-output")
|
251
|
-
|
252
|
-
if presigned_download_url:
|
253
|
-
output_file = f"results-{time.strftime('%Y-%m-%d-%Hh%Mm%Ss')}.zip"
|
254
|
-
|
255
|
-
output_file = os.path.join(output_directory, output_file)
|
256
|
-
os.makedirs(output_directory, exist_ok=True)
|
257
|
-
|
258
|
-
response = requests.get(presigned_download_url, timeout=60)
|
259
|
-
# write the zip to a file
|
260
|
-
with open(output_file, "wb") as f:
|
261
|
-
f.write(response.content)
|
262
|
-
|
263
|
-
# optionally, extract all the files from the zip
|
264
|
-
if do_unzip:
|
265
|
-
with zipfile.ZipFile(output_file, "r") as zip_ref:
|
266
|
-
zip_ref.extractall(output_file.replace(".zip", ""))
|
267
|
-
|
268
|
-
unzipped_state = "and unzipped" if do_unzip else "in zip format"
|
269
|
-
|
270
|
-
if self.verbose:
|
271
|
-
print(f"\tResults file downloaded {unzipped_state}.")
|
272
|
-
print(f"\t\tResults are available in: {output_directory}")
|
273
|
-
|
274
|
-
return output_directory
|
275
|
-
else:
|
276
|
-
return None
|
277
|
-
|
278
|
-
|
279
|
-
def main():
|
280
|
-
try:
|
281
|
-
print("Welcome to the NCA Engine Upload & Execution Demo")
|
282
|
-
args = CommandlineArgs()
|
283
|
-
files_path = os.path.join(Path(__file__).parent, "files")
|
284
|
-
|
285
|
-
args.analysis_file_default = os.path.join(files_path, "single_ev.csv")
|
286
|
-
args.config_file_default = os.path.join(
|
287
|
-
files_path, "configuration_single_ev.json"
|
288
|
-
)
|
289
|
-
args.metadata_file_default = os.path.join(files_path, "meta_data.json")
|
290
|
-
if not args.is_valid():
|
291
|
-
print("\n\n")
|
292
|
-
print("Missing some arguments.")
|
293
|
-
exit()
|
294
|
-
|
295
|
-
engine = NCAEngine(
|
296
|
-
api_domain=args.api_domain,
|
297
|
-
cognito_client_id=args.cognito_client_id,
|
298
|
-
region=args.aws_region,
|
299
|
-
)
|
300
|
-
|
301
|
-
print("\tLoading analysis configurations")
|
302
|
-
print(f"\t\t{args.config_file}")
|
303
|
-
config_data: dict = read_json_file(str(args.config_file))
|
304
|
-
|
305
|
-
print("\tLoading analysis meta data")
|
306
|
-
print(f"\t\t{args.metadata_file}")
|
307
|
-
meta_data = optional_json_loads(read_text_file(str(args.metadata_file)))
|
308
|
-
|
309
|
-
engine.execute(
|
310
|
-
username=str(args.username),
|
311
|
-
password=str(args.password),
|
312
|
-
input_file_path=str(args.analysis_file),
|
313
|
-
config_data=config_data,
|
314
|
-
meta_data=meta_data,
|
315
|
-
output_directory=str(args.output_directory),
|
316
|
-
)
|
317
|
-
print("Thank you for using the NCA Engine Upload and Execution Demo")
|
318
|
-
except Exception as e: # pylint: disable=w0718
|
319
|
-
print("An error occured ... exiting with an error")
|
320
|
-
print(str(e))
|
321
|
-
|
322
|
-
|
323
|
-
def optional_json_loads(data: str | dict) -> str | dict:
|
324
|
-
"""
|
325
|
-
Attempts to load the data as json, fails gracefull and retuns the data is if it fails
|
326
|
-
Args:
|
327
|
-
data (str): data as string
|
328
|
-
|
329
|
-
Returns:
|
330
|
-
str | dict: either the data as is or a converted dictionary/json object
|
331
|
-
"""
|
332
|
-
if isinstance(data, dict):
|
333
|
-
return data
|
334
|
-
|
335
|
-
try:
|
336
|
-
data = json.loads(str(data))
|
337
|
-
finally:
|
338
|
-
pass
|
339
|
-
return data
|
340
|
-
|
341
|
-
|
342
|
-
def read_json_file(file_path: str) -> dict:
|
343
|
-
"""
|
344
|
-
Reads a file and returns the json
|
345
|
-
Args:
|
346
|
-
file_path (str): _description_
|
347
|
-
|
348
|
-
Raises:
|
349
|
-
FileNotFoundError: _description_
|
350
|
-
|
351
|
-
Returns:
|
352
|
-
dict: _description_
|
353
|
-
"""
|
354
|
-
if not os.path.exists(file_path):
|
355
|
-
raise FileNotFoundError(f"File Not Found: {file_path}")
|
356
|
-
|
357
|
-
data = None
|
358
|
-
with open(file_path, mode="r", encoding="utf8") as file:
|
359
|
-
data = json.load(file)
|
360
|
-
|
361
|
-
return data
|
362
|
-
|
363
|
-
|
364
|
-
def read_text_file(file_path: str) -> str:
|
365
|
-
"""
|
366
|
-
Read files contents
|
367
|
-
Args:
|
368
|
-
file_path (str): path to the file
|
369
|
-
|
370
|
-
Raises:
|
371
|
-
FileNotFoundError: if the file is not found
|
372
|
-
|
373
|
-
Returns:
|
374
|
-
str: the files data
|
375
|
-
"""
|
376
|
-
if not os.path.exists(file_path):
|
377
|
-
raise FileNotFoundError(f"File Not Found: {file_path}")
|
378
|
-
|
379
|
-
data = None
|
380
|
-
with open(file_path, mode="r", encoding="utf8") as file:
|
381
|
-
data = file.read()
|
382
|
-
|
383
|
-
return data
|
384
|
-
|
385
|
-
|
386
|
-
if __name__ == "__main__":
|
387
|
-
main()
|