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.
Files changed (60) hide show
  1. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.gitignore +1 -0
  2. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/PKG-INFO +7 -6
  3. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/pyproject.toml +5 -5
  4. {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
  5. {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
  6. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/configs/file_upload.py +104 -0
  7. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.csv +13 -0
  8. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.data +13 -0
  9. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.sas7bdat +0 -0
  10. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xls +0 -0
  11. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xlsx +0 -0
  12. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/files/uploads/input1.xpt +0 -0
  13. {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
  14. {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
  15. {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
  16. {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
  17. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/integration_testing/tests/file_upload_test.py +150 -0
  18. {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
  19. {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
  20. aplos_nca_saas_sdk-0.0.9/src/aplos_nca_saas_sdk/nca_resources/nca_file_upload.py +57 -0
  21. {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
  22. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/version.py +1 -1
  23. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.pypirc +0 -0
  24. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.vscode/launch.json +0 -0
  25. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/.vscode/settings.json +0 -0
  26. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/LICENSE +0 -0
  27. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/README.md +0 -0
  28. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/pypi/build.py +0 -0
  29. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/pypi/readme.md +0 -0
  30. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/devops/requirements.txt +0 -0
  31. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/API_Configuration_blur.png +0 -0
  32. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/aplos-nca-commandline-cropped.png +0 -0
  33. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/docs/images/aplos-nca-commandline.png +0 -0
  34. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/mypy.ini +0 -0
  35. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/mypy_checks.py +0 -0
  36. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/readme.md +0 -0
  37. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/requirements.txt +0 -0
  38. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/run-checks.sh +0 -0
  39. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/src/aplos_nca_saas_sdk/__init__.py +0 -0
  40. {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
  41. {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
  42. {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
  43. {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
  44. {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
  45. {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
  46. {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
  47. {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
  48. {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
  49. {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
  50. {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
  51. {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
  52. {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
  53. {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
  54. {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
  55. {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
  56. {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
  57. {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
  58. {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
  59. {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
  60. {aplos_nca_saas_sdk-0.0.7 → aplos_nca_saas_sdk-0.0.9}/tests/__init__.py +0 -0
@@ -11,3 +11,4 @@ dist/
11
11
  # don't track environment files
12
12
  .env
13
13
  .env.*
14
+ .config.*
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: aplos_nca_saas_sdk
3
- Version: 0.0.7
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==1.34.110
21
+ Requires-Dist: boto3>=1.34.110
21
22
  Requires-Dist: mypy-boto3-cognito-idp>=1.35.68
22
- Requires-Dist: pyjwt==2.9.0
23
- Requires-Dist: python-dotenv==1.0.1
24
- Requires-Dist: requests==2.31.0
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.7"
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==2.31.0",
51
- "boto3==1.34.110",
52
- "python-dotenv==1.0.1",
53
- "PyJWT==2.9.0",
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) -> S3PresignedPayload:
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
- S3PresignedPayload: an instance of S3PresignedPayload
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 paylod
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 requried.
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(self, payload: S3PresignedPayload, input_file: str) -> bool:
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 True
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
@@ -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
- raise RuntimeError("This should be implemented by the subclass.")
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" Error: {test_result['errors']}")
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
- presign_payload: S3PresignedPayload = uploader.upload_file(input_file_path)
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=str(presign_payload.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 structor for a file"""
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
@@ -1,4 +1,4 @@
1
1
  # Aplos NCA SaaS SDK Version File
2
2
  # This is automatically generated during the build process.
3
3
  # DO NOT UPDATE IT DIRECTLY. IT WILL BE OVERWRITTEN.
4
- __version__ = '0.0.7'
4
+ __version__ = '0.0.9'