aplos-nca-saas-sdk 0.0.7__tar.gz → 0.0.9__tar.gz
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-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.gitignore +1 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/PKG-INFO +7 -6
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/pyproject.toml +5 -5
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/aws_resources/aws_s3_presigned_upload.py +14 -7
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/configs/config_sample.json +1 -7
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/configs/file_upload.py +104 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.csv +13 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.data +13 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.sas7bdat +0 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xls +0 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xlsx +0 -0
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xpt +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/integration_test_base.py +4 -2
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/integration_test_configurations.py +3 -1
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/integration_test_factory.py +2 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/integration_test_suite.py +1 -1
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/tests/file_upload_test.py +150 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/nca_resources/nca_endpoints.py +4 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/nca_resources/nca_executions.py +11 -5
- aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/nca_resources/nca_file_upload.py +57 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/utilities/environment_services.py +28 -1
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/version.py +1 -1
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.pypirc +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.vscode/launch.json +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.vscode/settings.json +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/LICENSE +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/README.md +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/pypi/build.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/pypi/readme.md +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/requirements.txt +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/API_Configuration_blur.png +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/aplos-nca-commandline-cropped.png +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/aplos-nca-commandline.png +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/mypy.ini +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/mypy_checks.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/readme.md +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/requirements.txt +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/run-checks.sh +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/__init__.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/aws_resources/aws_cognito.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/aws_resources/aws_s3_presigned_payload.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/configuration_single_ev.json +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/meta_data.json +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/files/analysis_files/single_ev/single_ev.csv +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/configs/_config_base.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/configs/app_settings.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/configs/login.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/integration_test_response.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/main.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/readme.md +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/tests/app_configuration_test.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/tests/app_execution_test.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/tests/app_login_test.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/integration_testing/tests/app_validation_test.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/nca_resources/nca_app_configuration.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/nca_resources/nca_login.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/utilities/commandline_args.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/utilities/environment_vars.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/utilities/http_utility.py +0 -0
- {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: aplos_nca_saas_sdk
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.9
|
4
4
|
Summary: Aplos NCA SaaS SDK
|
5
5
|
Project-URL: Homepage, https://aplosanalytics.com/
|
6
6
|
Project-URL: Documentation, https://docs.aplosanalytics.com/
|
@@ -8,6 +8,7 @@ Project-URL: Repository, https://github.com/AplosAnalytics/Aplos-NCA-SaaS-SDK
|
|
8
8
|
Project-URL: Issues, https://github.com/AplosAnalytics/Aplos-NCA-SaaS-SDK/issues
|
9
9
|
Author-email: Eric Wilson <eric.wilson@aplosanalytics.com>
|
10
10
|
License: MIT License
|
11
|
+
License-File: LICENSE
|
11
12
|
Keywords: Bioavailability Studies,Bioequivalence Studies,Clinical Pharmacology,Clinical Trials,Dose-Proportionality Studies,Drug Interaction Studies,NCA,Non-Compartmental Analysis,PK Analysis,PK modeling,Pharma Data Analysis,Pharmacokinetic Analysis,Pharmacokinetics,Pharmacokinetics Software,SaaS
|
12
13
|
Classifier: Development Status :: 4 - Beta
|
13
14
|
Classifier: Intended Audience :: Developers
|
@@ -17,11 +18,11 @@ Classifier: Programming Language :: Python :: 3
|
|
17
18
|
Classifier: Topic :: Software Development :: Libraries
|
18
19
|
Requires-Python: >=3.10
|
19
20
|
Requires-Dist: aws-lambda-powertools>=3.3.0
|
20
|
-
Requires-Dist: boto3
|
21
|
+
Requires-Dist: boto3>=1.34.110
|
21
22
|
Requires-Dist: mypy-boto3-cognito-idp>=1.35.68
|
22
|
-
Requires-Dist: pyjwt
|
23
|
-
Requires-Dist: python-dotenv
|
24
|
-
Requires-Dist: requests
|
23
|
+
Requires-Dist: pyjwt>=2.9.0
|
24
|
+
Requires-Dist: python-dotenv>=1.0.1
|
25
|
+
Requires-Dist: requests>=2.31.0
|
25
26
|
Description-Content-Type: text/markdown
|
26
27
|
|
27
28
|
# Aplos NCA SaaS SDK for python
|
@@ -26,7 +26,7 @@ keywords = [
|
|
26
26
|
"Clinical Trials"
|
27
27
|
]
|
28
28
|
|
29
|
-
version = "0.0.
|
29
|
+
version = "0.0.9"
|
30
30
|
authors = [
|
31
31
|
{ name="Eric Wilson", email="eric.wilson@aplosanalytics.com" }
|
32
32
|
]
|
@@ -47,10 +47,10 @@ classifiers = [
|
|
47
47
|
|
48
48
|
dependencies = [
|
49
49
|
"aws-lambda-powertools>=3.3.0",
|
50
|
-
"requests
|
51
|
-
"boto3
|
52
|
-
"python-dotenv
|
53
|
-
"PyJWT
|
50
|
+
"requests>=2.31.0",
|
51
|
+
"boto3>=1.34.110",
|
52
|
+
"python-dotenv>=1.0.1",
|
53
|
+
"PyJWT>=2.9.0",
|
54
54
|
"mypy_boto3_cognito_idp>=1.35.68"
|
55
55
|
]
|
56
56
|
|
@@ -6,6 +6,7 @@ Property of Aplos Analytics, Utah, USA
|
|
6
6
|
|
7
7
|
import json
|
8
8
|
import os
|
9
|
+
from typing import Any, Dict
|
9
10
|
|
10
11
|
import requests
|
11
12
|
from aplos_nca_saas_sdk.aws_resources.aws_s3_presigned_payload import (
|
@@ -21,7 +22,7 @@ class S3PresignedUpload:
|
|
21
22
|
self.api_url = api_url
|
22
23
|
self.jwt = jwt
|
23
24
|
|
24
|
-
def upload_file(self, input_file: str) ->
|
25
|
+
def upload_file(self, input_file: str) -> Dict[str, Any]:
|
25
26
|
"""
|
26
27
|
Uploads a file to your Aplos Cloud Account in AWS
|
27
28
|
|
@@ -29,7 +30,7 @@ class S3PresignedUpload:
|
|
29
30
|
input_file (str): path to the analysis file you are uploading
|
30
31
|
|
31
32
|
Returns:
|
32
|
-
|
33
|
+
Dictionary: including the file_id, status code and response information
|
33
34
|
"""
|
34
35
|
|
35
36
|
# get the presigned url for uploading
|
@@ -37,14 +38,14 @@ class S3PresignedUpload:
|
|
37
38
|
input_file=input_file
|
38
39
|
)
|
39
40
|
# upload the files
|
40
|
-
self.__upload_file_to_s3(paylod, input_file=input_file)
|
41
|
+
upload_response = self.__upload_file_to_s3(paylod, input_file=input_file)
|
41
42
|
|
42
|
-
return
|
43
|
+
return upload_response
|
43
44
|
|
44
45
|
def __get_presigned_upload_info(self, input_file: str) -> S3PresignedPayload:
|
45
46
|
"""
|
46
47
|
Performs all the necessary steps for creating a presigned url to upload a file to S3.
|
47
|
-
We're using AWS S3 presigned urls for security as well as allowing for very large files if
|
48
|
+
We're using AWS S3 presigned urls for security as well as allowing for very large files if required.
|
48
49
|
Args:
|
49
50
|
input_file (str): the path to the input (analysis) file
|
50
51
|
|
@@ -79,7 +80,9 @@ class S3PresignedUpload:
|
|
79
80
|
|
80
81
|
return payload
|
81
82
|
|
82
|
-
def __upload_file_to_s3(
|
83
|
+
def __upload_file_to_s3(
|
84
|
+
self, payload: S3PresignedPayload, input_file: str
|
85
|
+
) -> Dict[str, Any]:
|
83
86
|
"""
|
84
87
|
Peforms the actual uploading via a presigned url for S3 bucket storage
|
85
88
|
Args:
|
@@ -107,7 +110,11 @@ class S3PresignedUpload:
|
|
107
110
|
|
108
111
|
# Check the response: 204 is a success in this case
|
109
112
|
if upload_response and upload_response.status_code == 204:
|
110
|
-
return
|
113
|
+
return {
|
114
|
+
"status_code": upload_response.status_code,
|
115
|
+
"reason": upload_response.reason,
|
116
|
+
"file_id": payload.file_id,
|
117
|
+
}
|
111
118
|
else:
|
112
119
|
raise RuntimeError(
|
113
120
|
"Error uploading the file. "
|
@@ -53,10 +53,7 @@
|
|
53
53
|
},
|
54
54
|
"files": [
|
55
55
|
{
|
56
|
-
"file": "XXXXXXXXXXXXXXXXXXXXX"
|
57
|
-
"expected_results" : {
|
58
|
-
"status_code": 200
|
59
|
-
}
|
56
|
+
"file": "XXXXXXXXXXXXXXXXXXXXX"
|
60
57
|
},
|
61
58
|
{
|
62
59
|
"file": "XXXXXXXXXXXXXXXXXXXXX",
|
@@ -65,9 +62,6 @@
|
|
65
62
|
"username": "XXXXXXXXXXXXXXXXXXXXX",
|
66
63
|
"password": "XXXXXXXXXXXXXXXXXXXXX",
|
67
64
|
"domain": "XXXXXXXXXXXXXXXXXXXXX"
|
68
|
-
},
|
69
|
-
"expected_results" : {
|
70
|
-
"status_code": 403
|
71
65
|
}
|
72
66
|
}
|
73
67
|
]
|
@@ -0,0 +1,104 @@
|
|
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
|
@@ -0,0 +1,13 @@
|
|
1
|
+
subject,dose,time,conc,conc_obs
|
2
|
+
1,10000,0,0,BLQ
|
3
|
+
1,10000,0.5,0,BLQ
|
4
|
+
1,10000,1,0,BLQ
|
5
|
+
1,10000,1.5,0,BLQ
|
6
|
+
1,10000,2,0,BLQ
|
7
|
+
1,10000,4,449,449
|
8
|
+
1,10000,6,210,210
|
9
|
+
1,10000,8,286,286
|
10
|
+
1,10000,12,130,130
|
11
|
+
1,10000,16,43.4,43.4
|
12
|
+
1,10000,20,16.8,16.8
|
13
|
+
1,10000,24,10.6,10.6
|
@@ -0,0 +1,13 @@
|
|
1
|
+
subject,dose,time,conc,conc_obs
|
2
|
+
1,10000,0,0,BLQ
|
3
|
+
1,10000,0.5,0,BLQ
|
4
|
+
1,10000,1,0,BLQ
|
5
|
+
1,10000,1.5,0,BLQ
|
6
|
+
1,10000,2,0,BLQ
|
7
|
+
1,10000,4,449,449
|
8
|
+
1,10000,6,210,210
|
9
|
+
1,10000,8,286,286
|
10
|
+
1,10000,12,130,130
|
11
|
+
1,10000,16,43.4,43.4
|
12
|
+
1,10000,20,16.8,16.8
|
13
|
+
1,10000,24,10.6,10.6
|
aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.sas7bdat
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -5,6 +5,7 @@ Property of Aplos Analytics, Utah, USA
|
|
5
5
|
"""
|
6
6
|
|
7
7
|
from typing import Dict, Any, List
|
8
|
+
from abc import ABC, abstractmethod
|
8
9
|
|
9
10
|
from aplos_nca_saas_sdk.integration_testing.integration_test_configurations import (
|
10
11
|
TestConfiguration,
|
@@ -14,7 +15,7 @@ from aplos_nca_saas_sdk.integration_testing.integration_test_response import (
|
|
14
15
|
)
|
15
16
|
|
16
17
|
|
17
|
-
class IntegrationTestBase:
|
18
|
+
class IntegrationTestBase(ABC):
|
18
19
|
"""
|
19
20
|
Integration Test Base Class
|
20
21
|
"""
|
@@ -75,6 +76,7 @@ class IntegrationTestBase:
|
|
75
76
|
"""
|
76
77
|
return [result.error for result in self.results if result.error is not None]
|
77
78
|
|
79
|
+
@abstractmethod
|
78
80
|
def test(self) -> bool:
|
79
81
|
"""
|
80
82
|
Run the Test
|
@@ -85,4 +87,4 @@ class IntegrationTestBase:
|
|
85
87
|
of the tests fail, it will be false. Execeptions are only
|
86
88
|
raised if the raise_on_failure flag is set to True.
|
87
89
|
"""
|
88
|
-
|
90
|
+
pass
|
@@ -9,6 +9,7 @@ from typing import Any, Dict
|
|
9
9
|
from aplos_nca_saas_sdk.integration_testing.configs.app_settings import (
|
10
10
|
ApplicationSettings,
|
11
11
|
)
|
12
|
+
from aplos_nca_saas_sdk.integration_testing.configs.file_upload import FileUploads
|
12
13
|
from aplos_nca_saas_sdk.integration_testing.configs.login import Logins
|
13
14
|
|
14
15
|
|
@@ -21,7 +22,7 @@ class TestConfiguration:
|
|
21
22
|
def __init__(self):
|
22
23
|
self.app_config: ApplicationSettings = ApplicationSettings()
|
23
24
|
self.logins: Logins = Logins()
|
24
|
-
|
25
|
+
self.file_uploads: FileUploads = FileUploads()
|
25
26
|
def load(self, file_path: str):
|
26
27
|
"""
|
27
28
|
Loads the configuration from a file
|
@@ -36,3 +37,4 @@ class TestConfiguration:
|
|
36
37
|
|
37
38
|
self.logins.load(config.get("login_test", {}))
|
38
39
|
self.app_config.load(config.get("application_config_test", {}))
|
40
|
+
self.file_uploads.load(config.get("file_upload_test", {}))
|
@@ -4,6 +4,7 @@ All Rights Reserved. www.aplosanalytics.com LICENSED MATERIALS
|
|
4
4
|
Property of Aplos Analytics, Utah, USA
|
5
5
|
"""
|
6
6
|
|
7
|
+
from abc import ABC
|
7
8
|
import os
|
8
9
|
from typing import List
|
9
10
|
from pathlib import Path
|
@@ -12,6 +13,7 @@ import inspect
|
|
12
13
|
from aplos_nca_saas_sdk.integration_testing.integration_test_base import (
|
13
14
|
IntegrationTestBase,
|
14
15
|
)
|
16
|
+
from aplos_nca_saas_sdk.integration_testing.tests.file_upload_test import FileUploadTest
|
15
17
|
|
16
18
|
|
17
19
|
class IntegrationTestFactory:
|
@@ -102,7 +102,7 @@ class IntegrationTestSuite:
|
|
102
102
|
f" {test_result['test_name']} {'succeeded' if test_result['success'] else 'failed'} duration: {duration}"
|
103
103
|
)
|
104
104
|
if not test_result["success"]:
|
105
|
-
print(f"
|
105
|
+
print(f" Errors: {test_result['errors']}")
|
106
106
|
|
107
107
|
print(f"Test Suite completed in {datetime.now(UTC) - start_time}")
|
108
108
|
|
@@ -0,0 +1,150 @@
|
|
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 time
|
8
|
+
from datetime import datetime, timedelta
|
9
|
+
from typing import Any, Dict
|
10
|
+
|
11
|
+
import requests
|
12
|
+
|
13
|
+
from aplos_nca_saas_sdk.integration_testing.configs.login import Login
|
14
|
+
from aplos_nca_saas_sdk.integration_testing.integration_test_base import (
|
15
|
+
IntegrationTestBase,
|
16
|
+
)
|
17
|
+
from aplos_nca_saas_sdk.integration_testing.integration_test_response import (
|
18
|
+
IntegrationTestResponse,
|
19
|
+
)
|
20
|
+
from aplos_nca_saas_sdk.nca_resources.nca_file_upload import NCAFileUpload
|
21
|
+
from aplos_nca_saas_sdk.nca_resources.nca_login import NCALogin
|
22
|
+
from aplos_nca_saas_sdk.utilities.http_utility import HttpUtilities
|
23
|
+
|
24
|
+
|
25
|
+
class FileUploadTest(IntegrationTestBase):
|
26
|
+
"""File Upload Test Container"""
|
27
|
+
|
28
|
+
def __init__(self):
|
29
|
+
super().__init__("file-upload")
|
30
|
+
|
31
|
+
def test(self) -> bool:
|
32
|
+
"""Test file upload"""
|
33
|
+
|
34
|
+
self.results.clear()
|
35
|
+
|
36
|
+
for fileupload in self.config.file_uploads.list:
|
37
|
+
test_response: IntegrationTestResponse = IntegrationTestResponse()
|
38
|
+
test_response.name = self.name
|
39
|
+
try:
|
40
|
+
# Confirm Login
|
41
|
+
nca_login = self.__login(fileupload.login)
|
42
|
+
if not nca_login.jwt:
|
43
|
+
test_response.error = "Failed to authenticate"
|
44
|
+
else:
|
45
|
+
# Confirm Upload
|
46
|
+
upload_response: Dict[str, Any] = self.__upload(
|
47
|
+
nca_login, fileupload.filepath
|
48
|
+
)
|
49
|
+
if upload_response is None:
|
50
|
+
test_response.error = "Failed to upload"
|
51
|
+
else:
|
52
|
+
# Confirm conversion and download
|
53
|
+
# Allow time buffer so file data is available
|
54
|
+
time.sleep(3)
|
55
|
+
self.__download(
|
56
|
+
nca_login, upload_response.get("file_id"), test_response
|
57
|
+
)
|
58
|
+
|
59
|
+
except Exception as e: # pylint: disable=w0718
|
60
|
+
test_response.error = str(e)
|
61
|
+
|
62
|
+
self.results.append(test_response)
|
63
|
+
|
64
|
+
return self.success()
|
65
|
+
|
66
|
+
def __login(self, login: Login) -> NCALogin:
|
67
|
+
nca_login = NCALogin(aplos_saas_domain=login.domain)
|
68
|
+
nca_login.authenticate(username=login.username, password=login.password)
|
69
|
+
return nca_login
|
70
|
+
|
71
|
+
def __upload(self, nca_login: NCALogin, upload_file_path: str) -> Dict[str, Any]:
|
72
|
+
nca_file_upload = NCAFileUpload(nca_login)
|
73
|
+
upload_response: Dict[str, Any] = nca_file_upload.upload(upload_file_path)
|
74
|
+
return upload_response
|
75
|
+
|
76
|
+
def __download(
|
77
|
+
self,
|
78
|
+
nca_login: NCALogin,
|
79
|
+
file_id: str,
|
80
|
+
test_response: IntegrationTestResponse,
|
81
|
+
):
|
82
|
+
file_data_endpoint = nca_login.config.endpoints.file_data(
|
83
|
+
nca_login.cognito.tenant_id,
|
84
|
+
nca_login.cognito.user_id,
|
85
|
+
file_id,
|
86
|
+
)
|
87
|
+
file_info_endpoint = nca_login.config.endpoints.file(
|
88
|
+
nca_login.cognito.tenant_id,
|
89
|
+
nca_login.cognito.user_id,
|
90
|
+
file_id,
|
91
|
+
)
|
92
|
+
|
93
|
+
max_wait_in_minutes: int = 3
|
94
|
+
headers = HttpUtilities.get_headers(nca_login.jwt)
|
95
|
+
current_time = datetime.now()
|
96
|
+
|
97
|
+
# Create a timedelta object representing 3 minutes
|
98
|
+
time_delta = timedelta(minutes=max_wait_in_minutes)
|
99
|
+
# Add the timedelta to the current time
|
100
|
+
max_time = current_time + time_delta
|
101
|
+
|
102
|
+
complete = False
|
103
|
+
while not complete:
|
104
|
+
response = requests.get(file_info_endpoint, headers=headers, timeout=60)
|
105
|
+
json_response: dict = response.json()
|
106
|
+
errors = []
|
107
|
+
errors.extend(json_response.get("errors") or [])
|
108
|
+
status = json_response.get("workable_state")
|
109
|
+
complete = status == "ready"
|
110
|
+
|
111
|
+
if status == "invalid" or len(errors) > 0:
|
112
|
+
test_response.success = False
|
113
|
+
test_response.error = (
|
114
|
+
"File conversion failed."
|
115
|
+
if len(errors) < 0
|
116
|
+
else f"File conversion failed with errors ${errors}."
|
117
|
+
)
|
118
|
+
break
|
119
|
+
if complete:
|
120
|
+
break
|
121
|
+
if not complete:
|
122
|
+
time.sleep(5)
|
123
|
+
if datetime.now() > max_time:
|
124
|
+
test_response.success = False
|
125
|
+
test_response.error = (
|
126
|
+
"Timeout attempting to get conversion file status. "
|
127
|
+
f"The current timeout limit is {max_wait_in_minutes} minutes. "
|
128
|
+
"You may need to up the timeout period, or check for errors. "
|
129
|
+
)
|
130
|
+
break
|
131
|
+
|
132
|
+
if test_response.error is not None:
|
133
|
+
return
|
134
|
+
|
135
|
+
file_response = requests.get(file_data_endpoint, headers=headers, timeout=30)
|
136
|
+
|
137
|
+
json_file_response: dict = file_response.json()
|
138
|
+
data = json_file_response.get("data", None)
|
139
|
+
error = json_file_response.get("error", None)
|
140
|
+
|
141
|
+
if data is None:
|
142
|
+
test_response.success = False
|
143
|
+
test_response.error = "File download missing expected data. "
|
144
|
+
if error is not None:
|
145
|
+
test_response.success = False
|
146
|
+
test_response.error = (
|
147
|
+
test_response.error or ""
|
148
|
+
) + f"File download contained error ${error}"
|
149
|
+
|
150
|
+
test_response.success = True
|
@@ -52,3 +52,7 @@ class NCAEndpoints:
|
|
52
52
|
def file(self, tenant_id: str, user_id: str, file_id: str) -> str:
|
53
53
|
"""Returns the file endpoint"""
|
54
54
|
return f"{self.files(tenant_id=tenant_id, user_id=user_id)}/{file_id}"
|
55
|
+
|
56
|
+
def file_data(self, tenant_id: str, user_id: str, file_id: str) -> str:
|
57
|
+
"""Returns get file data endpoint"""
|
58
|
+
return f"{self.__base(tenant_id=tenant_id, user_id=user_id)}/nca/files/{file_id}/data"
|
@@ -10,12 +10,11 @@ import time
|
|
10
10
|
import zipfile
|
11
11
|
from datetime import datetime, timedelta
|
12
12
|
from pathlib import Path
|
13
|
+
from typing import Any, Dict
|
13
14
|
|
14
15
|
import requests
|
16
|
+
|
15
17
|
from aplos_nca_saas_sdk.aws_resources.aws_cognito import CognitoAuthenication
|
16
|
-
from aplos_nca_saas_sdk.aws_resources.aws_s3_presigned_payload import (
|
17
|
-
S3PresignedPayload,
|
18
|
-
)
|
19
18
|
from aplos_nca_saas_sdk.aws_resources.aws_s3_presigned_upload import (
|
20
19
|
S3PresignedUpload,
|
21
20
|
)
|
@@ -90,12 +89,12 @@ class NCAEngine:
|
|
90
89
|
if self.verbose:
|
91
90
|
print("\tUploading the analysis file.")
|
92
91
|
uploader: S3PresignedUpload = S3PresignedUpload(self.jwt, str(self.api_root))
|
93
|
-
|
92
|
+
upload_response: Dict[str, Any] = uploader.upload_file(input_file_path)
|
94
93
|
|
95
94
|
if self.verbose:
|
96
95
|
print("\tStarting the execution.")
|
97
96
|
execution_id = self.run_analysis(
|
98
|
-
file_id=
|
97
|
+
file_id=upload_response.get("file_id", ""),
|
99
98
|
config_data=config_data,
|
100
99
|
meta_data=meta_data,
|
101
100
|
)
|
@@ -131,6 +130,13 @@ class NCAEngine:
|
|
131
130
|
str: the execution id
|
132
131
|
"""
|
133
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
|
+
)
|
134
140
|
headers = HttpUtilities.get_headers(self.jwt)
|
135
141
|
# to start a new execution we need the location of the file (s3 bucket and object key)
|
136
142
|
# you basic configuration
|
@@ -0,0 +1,57 @@
|
|
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 Any, Dict
|
8
|
+
from aplos_nca_saas_sdk.aws_resources.aws_s3_presigned_upload import S3PresignedUpload
|
9
|
+
from aplos_nca_saas_sdk.nca_resources.nca_login import NCALogin
|
10
|
+
from aplos_nca_saas_sdk.utilities.http_utility import HttpUtilities
|
11
|
+
|
12
|
+
|
13
|
+
class NCAFileUpload:
|
14
|
+
"""NCA File Upload"""
|
15
|
+
|
16
|
+
def __init__(self, nca_login: NCALogin) -> None:
|
17
|
+
if nca_login is None or nca_login.jwt is None or not nca_login.jwt:
|
18
|
+
raise ValueError("Authenticated nca_login is required.")
|
19
|
+
|
20
|
+
self.__api_domain: str = nca_login.domain
|
21
|
+
self.__tenant_id: str = nca_login.cognito.tenant_id
|
22
|
+
self.__user_id: str = nca_login.cognito.user_id
|
23
|
+
self.__jwt: str = nca_login.jwt
|
24
|
+
|
25
|
+
@property
|
26
|
+
def api_root(self) -> str:
|
27
|
+
"""Gets the base url"""
|
28
|
+
|
29
|
+
if self.__api_domain is None:
|
30
|
+
raise RuntimeError("Missing Aplos Api Domain")
|
31
|
+
|
32
|
+
url = HttpUtilities.build_url(self.__api_domain)
|
33
|
+
if isinstance(url, str):
|
34
|
+
return f"{url}/tenants/{self.__tenant_id}/users/{self.__user_id}"
|
35
|
+
|
36
|
+
raise RuntimeError("Missing Aplos Api Domain")
|
37
|
+
|
38
|
+
def upload(self, input_file_path: str) -> Dict[str, Any]:
|
39
|
+
"""
|
40
|
+
Uploads a file to the Aplos NCA Cloud
|
41
|
+
|
42
|
+
Args:
|
43
|
+
input_file_path (str): local path to the file
|
44
|
+
|
45
|
+
Raises:
|
46
|
+
ValueError: _description_
|
47
|
+
|
48
|
+
Returns:
|
49
|
+
Dict: {"file_id": id, "statu_code": 204}
|
50
|
+
"""
|
51
|
+
if input_file_path is None or not input_file_path:
|
52
|
+
raise ValueError("Valid input_file_path is required.")
|
53
|
+
|
54
|
+
uploader: S3PresignedUpload = S3PresignedUpload(self.__jwt, str(self.api_root))
|
55
|
+
upload_response: Dict[str, Any] = uploader.upload_file(input_file_path)
|
56
|
+
|
57
|
+
return upload_response
|
@@ -59,7 +59,7 @@ class EnvironmentServices:
|
|
59
59
|
def find_file(
|
60
60
|
self, starting_path: str, file_name: str, raise_error_if_not_found: bool = True
|
61
61
|
) -> str | None:
|
62
|
-
"""Searches the project directory
|
62
|
+
"""Searches the project directory structure for a file"""
|
63
63
|
parents = 10
|
64
64
|
starting_path = starting_path or __file__
|
65
65
|
|
@@ -79,3 +79,30 @@ class EnvironmentServices:
|
|
79
79
|
)
|
80
80
|
|
81
81
|
return None
|
82
|
+
|
83
|
+
def find_module_path(
|
84
|
+
self,
|
85
|
+
starting_path: str | None = None,
|
86
|
+
raise_error_if_not_found: bool = True,
|
87
|
+
) -> str | None:
|
88
|
+
"""From a given starting point, move up the directory chain until you find the modules root"""
|
89
|
+
parents = 10
|
90
|
+
starting_path = starting_path or __file__
|
91
|
+
MODULE_ROOT = "aplos_nca_saas_sdk" # pylint: disable=c0103
|
92
|
+
paths: List[str] = []
|
93
|
+
for parent in range(parents):
|
94
|
+
path = Path(starting_path).parents[parent].absolute()
|
95
|
+
|
96
|
+
# get the directory name
|
97
|
+
directory_name = os.path.basename(path)
|
98
|
+
if directory_name == MODULE_ROOT:
|
99
|
+
# return the full path
|
100
|
+
return str(path)
|
101
|
+
|
102
|
+
if raise_error_if_not_found:
|
103
|
+
searched_paths = "\n".join(paths)
|
104
|
+
raise RuntimeError(
|
105
|
+
f"Failed to locate the moduel root: {MODULE_ROOT} in: \n {searched_paths}"
|
106
|
+
)
|
107
|
+
|
108
|
+
return None
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/API_Configuration_blur.png
RENAMED
File without changes
|
{aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/aplos-nca-commandline-cropped.png
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|