hdforce 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
hdforce/AuthManager.py ADDED
@@ -0,0 +1,170 @@
1
+ import os
2
+ from dotenv import load_dotenv, set_key
3
+ # Package imports
4
+ from .LoggerConfig import LoggerConfig
5
+ from .utils import TokenManager, varsManager, ConfigManager
6
+
7
+ # Get a logger specific to this module
8
+ logger = LoggerConfig.get_logger(__name__)
9
+
10
+ # -------------------- #
11
+ # Authenticator
12
+
13
+
14
+ def AuthManager(authMethod: str = "env", refreshToken_name: str = "HD_REFRESH_TOKEN", refreshToken: str = None, env_file_name: str = None, region: str = "Americas") -> None:
15
+ """ Choose the authentication settings
16
+
17
+
18
+ Parameters
19
+ ----------
20
+ region : str required
21
+ The region that designates the url prefix.
22
+
23
+ authMethod : str required
24
+ Determine method of storing authentication variables, including refresh token. One of 'env', 'file', 'manual'. env = use of system environment. file = use of .env file. manual = no stored refresh token.
25
+
26
+ refreshToken_name : str
27
+ Specific name of refresh token variable saved in system environment or .env file
28
+
29
+ refreshToken : str
30
+ If used with authMethod='manual', token will be used to authenticate without being stored. Else token will be used set as the new refresh token value with method selected.
31
+
32
+ env_file_name : str
33
+ Required with authMethod='file'. Provides file name for variable storage.
34
+
35
+
36
+ Raises
37
+ ------
38
+ ValueError
39
+ If authMethod not one of env, file, or method.
40
+ If authMethod = 'file' and no file name provided
41
+ If authMethod='manual' and no refreshToken provided
42
+
43
+ """
44
+ # auth method options
45
+ methods = ['env', 'file', 'manual']
46
+
47
+ # Check for valid method choice
48
+ if authMethod not in methods:
49
+ logger.error(f"{authMethod} is invalid authentication method.")
50
+ raise ValueError(f"Invalid auth method. Allowed methods are: {methods}")
51
+ else:
52
+ logger.debug(f"Authentication method selected: {authMethod}.")
53
+
54
+ # Load environment file if necessary
55
+ if authMethod == 'file':
56
+ if env_file_name:
57
+ load_dotenv(str(env_file_name), override=True)
58
+ logger.debug(f"Environment variables loaded from {env_file_name}.")
59
+ else:
60
+ logger.error("No file name given")
61
+ raise ValueError("No file name given. Must be provided with authMethod='file'")
62
+
63
+ # Retrieve or set the refresh token
64
+ if authMethod == 'manual':
65
+ if refreshToken is None:
66
+ logger.error("No refresh token found given")
67
+ raise ValueError("Refresh token must be provided with 'manual' authentication method.")
68
+ else:
69
+ key = refreshToken
70
+ kabrv = key[0:6]
71
+ #logger.debug(f"Manual auth method used with token {kabrv}xxxx")
72
+ else:
73
+ key = varsManager(name=refreshToken_name, value=refreshToken, method=authMethod, file=env_file_name)
74
+ kabrv = key[0:6]
75
+ logger.debug(f"Refresh token: {kabrv}xxxx method: {authMethod}")
76
+
77
+ # Setup session assuming TokenManager handles authentication
78
+ session = TokenManager(refreshToken=key, region=region, fileName=env_file_name)
79
+
80
+ # Create objects of classes
81
+ accessToken = str(session.accessToken)
82
+ tokenExpiration = str(session.ExpirationVal)
83
+ expirationStr = str(session.ExpirationStr)
84
+ cloudURL = str(session.url_cloud)
85
+ fileName = str(session.fileName)
86
+
87
+ if authMethod == 'file':
88
+ load_dotenv(str(fileName), override=True)
89
+
90
+ # Set environment variables
91
+ set_key(fileName, "ACCESS_TOKEN", accessToken)
92
+ # Environment variable debugging: Access Token
93
+ if accessToken == os.getenv("ACCESS_TOKEN"):
94
+ logger.debug(f"New Access Token Set")
95
+ else:
96
+ logger.debug(f"Error: new token not found")
97
+
98
+ # Environment variable debugging: Token Expiration
99
+ set_key(fileName, "TOKEN_EXPIRATION", tokenExpiration)
100
+ if tokenExpiration == os.getenv("TOKEN_EXPIRATION"):
101
+ logger.debug(f"New expiration Set")
102
+ else:
103
+ logger.debug(f"Error: new expiration not passed")
104
+
105
+ # Environment variable debugging: Cloud URL
106
+ set_key(fileName, "CLOUD_URL", cloudURL)
107
+ if cloudURL == os.getenv("CLOUD_URL"):
108
+ logger.debug(f"New URL set")
109
+ else:
110
+ logger.debug(f"Error: new URL not passed")
111
+
112
+ else:
113
+ # Environment variable debugging: Access Token
114
+ os.environ['ACCESS_TOKEN'] = accessToken
115
+ if accessToken == os.getenv("ACCESS_TOKEN"):
116
+ logger.debug(f"New Access Token Set")
117
+ else:
118
+ logger.debug(f"Error: new token not found")
119
+
120
+ # Environment variable debugging: Token Expiration
121
+ os.environ['TOKEN_EXPIRATION'] = tokenExpiration
122
+ if tokenExpiration == os.getenv("TOKEN_EXPIRATION"):
123
+ logger.debug(f"New expiration Set")
124
+ else:
125
+ logger.debug(f"Error: new expiration not passed")
126
+
127
+ # Environment variable debugging: Cloud URL
128
+ os.environ['CLOUD_URL'] = cloudURL
129
+ if cloudURL == os.getenv("CLOUD_URL"):
130
+ logger.debug(f"New URL set")
131
+ else:
132
+ logger.debug(f"Error: new URL not passed")
133
+
134
+ # Set environment source in ConfigManager based on authMethod
135
+ if authMethod == 'file':
136
+ # Check if filename passed
137
+ if not isinstance(fileName, str):
138
+ logger.error("File path must be provided when source is 'file'")
139
+ raise ValueError("File path must be provided when source is 'file'")
140
+ # if filename exists and string initialize dotenv
141
+ load_dotenv(str(fileName), override=True)
142
+ # run configuration manager
143
+ ConfigManager.set_env_source(region=region, method=authMethod, fileName=fileName, token_name=refreshToken_name, token=key)
144
+ logger.debug(f"ConfigManager methods passed with file env: {fileName}")
145
+
146
+ # Using environment variables
147
+ elif authMethod == 'env' or 'manual':
148
+ # run configuration manager
149
+ ConfigManager.set_env_source(region=region, method=authMethod, fileName=fileName, token_name=refreshToken_name, token=key)
150
+ logger.debug(f"ConfigManager methods passed with {authMethod} method")
151
+
152
+ # Alert of missing variables
153
+ else:
154
+ # List to hold the names of empty variables
155
+ empty_variables = []
156
+
157
+ # Check each variable and add the name to the list if it is empty
158
+ if not accessToken:
159
+ empty_variables.append("Access Token")
160
+ if not tokenExpiration:
161
+ empty_variables.append("token Expiration")
162
+ if not cloudURL:
163
+ empty_variables.append("Cloud URL")
164
+
165
+ # Create a message based on which variables are empty
166
+ if empty_variables:
167
+ # Join the names of the empty variables into a comma-separated string
168
+ empty_vars_str = ", ".join(empty_variables)
169
+ logger.error(f"Missing variables: {empty_vars_str}.")
170
+ raise ValueError(f"The following variables are empty: {empty_vars_str}.")
hdforce/GetAthletes.py ADDED
@@ -0,0 +1,124 @@
1
+ # Dependencies -----
2
+ import requests
3
+ import os
4
+ import datetime
5
+ import pandas as pd
6
+ # Package imports
7
+ from .AuthManager import AuthManager
8
+ from .utils import ConfigManager
9
+ from .LoggerConfig import LoggerConfig
10
+
11
+ # Get a logger specific to this module
12
+ logger = LoggerConfig.get_logger(__name__)
13
+
14
+ # -------------------- #
15
+ # Get Athletes
16
+
17
+
18
+ def GetAthletes(inactive: bool = False) -> pd.DataFrame:
19
+ """Get the athlete information from an account.
20
+
21
+ Parameters
22
+ ----------
23
+ inactive : bool, optional
24
+ A boolean that specifies whether to include inactive athletes in the results. Default is False, meaning by default inactive athletes are not included.
25
+
26
+ Returns
27
+ -------
28
+ pd.DataFrame
29
+ A Pandas DataFrame containing the athletes' information, with columns:
30
+ - id: Athlete's unique identifier.
31
+ - names: Athlete's given full name.
32
+ - teams: A nested list of athlete's team ids as strings.
33
+ - groups: A nested list of athlete's group ids as strings.
34
+ - active: Boolean indicating if the athlete's profile is active (not archived).
35
+ - external: Columns dynamically created for each external attribute associated with the athletes. (example = external.ExternalId: value)
36
+
37
+ Raises
38
+ ------
39
+ Exception
40
+ If the HTTP response status is not 200, indicating an unsuccessful API request, or if there is a failure in parsing the JSON response.
41
+ """
42
+ # Retrieve Access Token and check expiration
43
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
44
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
45
+ logger.debug(f"Access Token retrieved. expires {datetime.datetime.fromtimestamp(tokenExp)}")
46
+
47
+ # get current time in timestamp
48
+ now = datetime.datetime.now()
49
+ nowtime = datetime.datetime.timestamp(now)
50
+ if nowtime < tokenExp:
51
+ logger.debug(f"Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
52
+
53
+ # Validate refresh token and expiration
54
+ if a_token is None:
55
+ logger.error("No Access Token found.")
56
+ raise Exception("No Access Token found.")
57
+ elif int(nowtime) >= tokenExp:
58
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
59
+ # authenticate
60
+ try:
61
+ AuthManager(
62
+ region=ConfigManager.region,
63
+ authMethod=ConfigManager.env_method,
64
+ refreshToken_name=ConfigManager.token_name,
65
+ refreshToken=ConfigManager.refresh_token,
66
+ env_file_name=ConfigManager.file_name
67
+ )
68
+ # Retrieve Access Token and check expiration
69
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
70
+ logger.debug("New ACCESS_TOKEN retrieved")
71
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
72
+ logger.debug("TOKEN_EXPIRATION retrieved")
73
+ if a_token is None:
74
+ logger.error("No Access Token found.")
75
+ raise Exception("No Access Token found.")
76
+ elif int(nowtime) >= tokenExp:
77
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
78
+ raise Exception("Token expired")
79
+ else:
80
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
81
+ pass
82
+ except ValueError:
83
+ logger.error("Failed to authenticate. Try AuthManager")
84
+ raise Exception("Failed to authenticate. Try AuthManage")
85
+ else:
86
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
87
+
88
+ # API Cloud URL
89
+ url_cloud = os.getenv("CLOUD_URL")
90
+
91
+ # Create URL for request
92
+ url = f"{url_cloud}/athletes?inactive={inactive}"
93
+
94
+ # GET Request
95
+ headers = {"Authorization": f"Bearer {a_token}"}
96
+
97
+ # Create Response
98
+ response = requests.get(url, headers=headers)
99
+ if inactive:
100
+ logger.debug("GET Request: Athletes (inactive = true)")
101
+ else:
102
+ logger.debug("GET Request: Athletes (inactive = false)")
103
+
104
+ # Response Handling
105
+ # If Error show error
106
+ if response.status_code != 200:
107
+ logger.error(f"Error {response.status_code}: {response.reason}")
108
+ raise Exception(f"Error {response.status_code}: {response.reason}")
109
+
110
+ # If successful
111
+ try:
112
+ data = response.json()['data']
113
+ df = pd.json_normalize(data, meta=['count'], errors='ignore')
114
+
115
+ # Setting attributes
116
+ df.attrs['Count'] = int(len(df.index))
117
+ count = str(len(df.index))
118
+ logger.info(f"Request successful. Athletes returned: {count}")
119
+ return df
120
+
121
+ # Bad parse or none returned
122
+ except ValueError:
123
+ logger.error("Failed to parse JSON response or no data returned.")
124
+ raise Exception("Failed to parse JSON response or no data returned.")
@@ -0,0 +1,140 @@
1
+ # Dependencies -----
2
+ import requests
3
+ import pandas as pd
4
+ import os
5
+ import datetime
6
+ # Package imports
7
+ from .utils import logger, ConfigManager
8
+ from .AuthManager import AuthManager
9
+
10
+ # -------------------- #
11
+ # Get Force Time
12
+
13
+
14
+ def GetForceTime(testId: str) -> pd.DataFrame:
15
+ """Get force-time data for an individual test trial from an account.
16
+
17
+ Parameters
18
+ ----------
19
+ testId : str
20
+ The unique ID given to each test trial.
21
+
22
+ Returns
23
+ -------
24
+ pd.DataFrame
25
+ A Pandas DataFrame containing details of the test trial, with columns:
26
+ - Time (s): Time elapsed in seconds.
27
+ - LeftForce (N): Force at time point from left plate.
28
+ - RightForce (N): Force at time point from right plate.
29
+ - CombinedForce (N): Combined force (Left + Right) at each time point.
30
+ - Velocity (m/s): Calculated center of mass velocity at each time point.
31
+ - Displacement (m): Calculated center of mass displacement from starting height at each time point.
32
+ - Power (W): Calculated power of mass at each time point.
33
+ - RSI: Calculated Reactive Strength Index (if applicable).
34
+
35
+ Raises
36
+ ------
37
+ Exception
38
+ If the HTTP response status is not 200, indicating an unsuccessful API request,
39
+ or if there is a failure in parsing the JSON response.
40
+ ValueError
41
+ If the 'testId' parameter is not a string.
42
+ """
43
+ # Retrieve Access Token and check expiration
44
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
45
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
46
+ logger.debug(f"Access Token retrieved. expires {datetime.datetime.fromtimestamp(tokenExp)}")
47
+
48
+ # get current time in timestamp
49
+ now = datetime.datetime.now()
50
+ nowtime = datetime.datetime.timestamp(now)
51
+ if nowtime < tokenExp:
52
+ logger.debug(f"Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
53
+
54
+ # Validate refresh token and expiration
55
+ if a_token is None:
56
+ logger.error("No Access Token found.")
57
+ raise Exception("No Access Token found.")
58
+ elif int(nowtime) >= tokenExp:
59
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
60
+ # authenticate
61
+ try:
62
+ AuthManager(
63
+ region=ConfigManager.region,
64
+ authMethod=ConfigManager.env_method,
65
+ refreshToken_name=ConfigManager.token_name,
66
+ refreshToken=ConfigManager.refresh_token,
67
+ env_file_name=ConfigManager.file_name
68
+ )
69
+ # Retrieve Access Token and check expiration
70
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
71
+ logger.debug("New ACCESS_TOKEN retrieved")
72
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
73
+ logger.debug("TOKEN_EXPIRATION retrieved")
74
+ if a_token is None:
75
+ logger.error("No Access Token found.")
76
+ raise Exception("No Access Token found.")
77
+ elif int(nowtime) >= tokenExp:
78
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
79
+ raise Exception("Token expired")
80
+ else:
81
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
82
+ pass
83
+ except ValueError:
84
+ logger.error("Failed to authenticate. Try AuthManager")
85
+ raise Exception("Failed to authenticate. Try AuthManage")
86
+ else:
87
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
88
+
89
+ # API Cloud URL
90
+ url_cloud = os.getenv("CLOUD_URL")
91
+
92
+ # Test ID
93
+ if isinstance(testId, str):
94
+ tid = testId
95
+ else:
96
+ logger.error("TestId must be a string")
97
+ raise Exception("Error: TestId must be a string")
98
+
99
+ # Create URL for request
100
+ url = f"{url_cloud}/forcetime/{tid}"
101
+
102
+ # GET Request
103
+ headers = {"Authorization": f"Bearer {a_token}"}
104
+ response = requests.get(url, headers=headers)
105
+ logger.debug(f"GET Force-Time data for test: {tid}")
106
+
107
+ # Check response status and handle data accordingly
108
+ if response.status_code != 200:
109
+ logger.error(f"Error {response.status_code}: {response.reason}")
110
+ raise Exception(f"Error {response.status_code}: {response.reason}")
111
+
112
+ try:
113
+ # Flatten test data from response
114
+ data = response.json()
115
+
116
+ # Create DataFrame from the array data
117
+ df = pd.DataFrame({
118
+ "Time(s)": data["Time(s)"],
119
+ "LeftForce(N)": data["LeftForce(N)"],
120
+ "RightForce(N)": data["RightForce(N)"],
121
+ "CombinedForce(N)": data["CombinedForce(N)"],
122
+ "Velocity(m/s)": data["Velocity(m/s)"],
123
+ "Displacement(m)": data["Displacement(m)"],
124
+ "Power(W)": data["Power(W)"],
125
+ "rsi": [data["rsi"]] * len(data["Time(s)"]) # Assuming rsi is a constant value
126
+ })
127
+
128
+ # Setting attributes
129
+ df.attrs['Test ID'] = data['id']
130
+ df.attrs['Test Name'] = data['testType']['name']
131
+ df.attrs['Athlete Name'] = data['athlete']['name']
132
+ df.attrs['Athlete ID'] = data['athlete']['id']
133
+ df.attrs['Timestamp'] = pd.to_datetime(data['timestamp'], unit='s')
134
+ df.attrs['RSI'] = data['rsi']
135
+ logger.info(f"Request successful: {df.attrs['Test Name']} - {df.attrs['Test ID']} - {df.attrs['Timestamp']}")
136
+ return df
137
+
138
+ except ValueError:
139
+ logger.error("Failed to parse JSON response or no data returned.")
140
+ raise Exception("Failed to parse JSON response or no data returned.")
hdforce/GetGroups.py ADDED
@@ -0,0 +1,107 @@
1
+ # Dependencies -----
2
+ import requests
3
+ import os
4
+ import datetime
5
+ import pandas as pd
6
+ # Package imports
7
+ from .utils import logger, ConfigManager
8
+ from .AuthManager import AuthManager
9
+
10
+ # -------------------- #
11
+ # Get Groups -----
12
+
13
+
14
+ def GetGroups() -> pd.DataFrame:
15
+ """Get group for an account. This function is designed to retrieve all groups within your organization.
16
+
17
+ Returns
18
+ -------
19
+ pd.DataFrame
20
+ A Pandas DataFrame containing the groups' information, with columns:
21
+ - id: Group's unique identifier.
22
+ - name: Group's name.
23
+
24
+ Raises
25
+ ------
26
+ Exception
27
+ If the HTTP response status is not 200, indicating an unsuccessful API request, or if there is a failure in parsing the JSON response.
28
+ """
29
+ # Retrieve Access Token and check expiration
30
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
31
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
32
+ logger.debug(f"Access Token retrieved. expires {datetime.datetime.fromtimestamp(tokenExp)}")
33
+
34
+ # get current time in timestamp
35
+ now = datetime.datetime.now()
36
+ nowtime = datetime.datetime.timestamp(now)
37
+ if nowtime < tokenExp:
38
+ logger.debug(f"Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
39
+
40
+ # Validate refresh token and expiration
41
+ if a_token is None:
42
+ logger.error("No Access Token found.")
43
+ raise Exception("No Access Token found.")
44
+ elif int(nowtime) >= tokenExp:
45
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
46
+ # authenticate
47
+ try:
48
+ AuthManager(
49
+ region=ConfigManager.region,
50
+ authMethod=ConfigManager.env_method,
51
+ refreshToken_name=ConfigManager.token_name,
52
+ refreshToken=ConfigManager.refresh_token,
53
+ env_file_name=ConfigManager.file_name
54
+ )
55
+ # Retrieve Access Token and check expiration
56
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
57
+ logger.debug("New ACCESS_TOKEN retrieved")
58
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
59
+ logger.debug("TOKEN_EXPIRATION retrieved")
60
+ if a_token is None:
61
+ logger.error("No Access Token found.")
62
+ raise Exception("No Access Token found.")
63
+ elif int(nowtime) >= tokenExp:
64
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
65
+ raise Exception("Token expired")
66
+ else:
67
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
68
+ pass
69
+ except ValueError:
70
+ logger.error("Failed to authenticate. Try AuthManager")
71
+ raise Exception("Failed to authenticate. Try AuthManage")
72
+ else:
73
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
74
+
75
+ # API Cloud URL
76
+ url_cloud = os.getenv("CLOUD_URL")
77
+
78
+ # Create URL for request
79
+ url = f"{url_cloud}/groups"
80
+
81
+ # GET Request
82
+ headers = {"Authorization": f"Bearer {a_token}"}
83
+
84
+ # Create Response
85
+ response = requests.get(url, headers=headers)
86
+ logger.debug("GET Request: Groups")
87
+
88
+ # Response Handling
89
+ # If Error show error
90
+ if response.status_code != 200:
91
+ logger.error(f"Error {response.status_code}: {response.reason}")
92
+ raise Exception(f"Error {response.status_code}: {response.reason}")
93
+
94
+ # If successful
95
+ try:
96
+ # Flatten test data from response
97
+ data = response.json()
98
+ df = pd.json_normalize(data['data'], meta=['count'], errors='ignore')
99
+
100
+ df.attrs['Count'] = int(len(df.index))
101
+ count = str(len(df.index))
102
+ logger.info(f"Request successful. Groups returned: {count}")
103
+ return df
104
+
105
+ except ValueError:
106
+ logger.error("Failed to parse JSON response or no data returned.")
107
+ raise Exception("Failed to parse JSON response or no data returned.")
hdforce/GetMetrics.py ADDED
@@ -0,0 +1,118 @@
1
+ # Dependencies -----
2
+ import requests
3
+ import os
4
+ import datetime
5
+ import pandas as pd
6
+ # Package imports
7
+ from .utils import logger, ConfigManager
8
+ from .AuthManager import AuthManager
9
+
10
+ # ----------------- #
11
+ # Get Metrics
12
+
13
+
14
+ def GetMetrics() -> pd.DataFrame:
15
+ """
16
+ Get the metrics and ids for all the metrics in the system
17
+
18
+ Returns
19
+ -------
20
+ pd.DataFrame
21
+ A Pandas DataFrame containing the test metrics, with columns:
22
+ - canonicalTestTypeId: The unique identifier for each test type.
23
+ - testTypeName: The name of each metric.
24
+ - id: The unique identifier for each metric.
25
+ - label: The label (common name) for each metric
26
+ - units: Units of measure
27
+ - description: Full description of metric and calculation*
28
+
29
+ Raises
30
+ ------
31
+ Exception
32
+ If the HTTP response status is not 200, indicating an unsuccessful API request, or if there is a failure in parsing the JSON response.
33
+ """
34
+ # Retrieve Access Token and check expiration
35
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
36
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
37
+ logger.debug(f"Access Token retrieved. expires {datetime.datetime.fromtimestamp(tokenExp)}")
38
+
39
+ # get current time in timestamp
40
+ now = datetime.datetime.now()
41
+ nowtime = datetime.datetime.timestamp(now)
42
+ if nowtime < tokenExp:
43
+ logger.debug(f"Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
44
+
45
+ # Validate refresh token and expiration
46
+ if a_token is None:
47
+ logger.error("No Access Token found.")
48
+ raise Exception("No Access Token found.")
49
+ elif int(nowtime) >= tokenExp:
50
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
51
+ # authenticate
52
+ try:
53
+ AuthManager(
54
+ region=ConfigManager.region,
55
+ authMethod=ConfigManager.env_method,
56
+ refreshToken_name=ConfigManager.token_name,
57
+ refreshToken=ConfigManager.refresh_token,
58
+ env_file_name=ConfigManager.file_name
59
+ )
60
+ # Retrieve Access Token and check expiration
61
+ a_token = ConfigManager.get_env_variable("ACCESS_TOKEN")
62
+ logger.debug("New ACCESS_TOKEN retrieved")
63
+ tokenExp = int(ConfigManager.get_env_variable("TOKEN_EXPIRATION"))
64
+ logger.debug("TOKEN_EXPIRATION retrieved")
65
+ if a_token is None:
66
+ logger.error("No Access Token found.")
67
+ raise Exception("No Access Token found.")
68
+ elif int(nowtime) >= tokenExp:
69
+ logger.debug(f"Token Expired: {datetime.datetime.fromtimestamp(tokenExp)}")
70
+ raise Exception("Token expired")
71
+ else:
72
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
73
+ pass
74
+ except ValueError:
75
+ logger.error("Failed to authenticate. Try AuthManager")
76
+ raise Exception("Failed to authenticate. Try AuthManage")
77
+ else:
78
+ logger.debug(f"New Access Token valid through: {datetime.datetime.fromtimestamp(tokenExp)}")
79
+
80
+ # API Cloud URL
81
+ url_cloud = os.getenv("CLOUD_URL")
82
+
83
+ # Create URL for request
84
+ url = f"{url_cloud}/metrics"
85
+
86
+ # GET Request
87
+ headers = {"Authorization": f"Bearer {a_token}"}
88
+
89
+ # Create Response
90
+ response = requests.get(url, headers=headers)
91
+ logger.debug("GET Request: Metrics.")
92
+
93
+ # Response Handling
94
+ # If Error show error
95
+ if response.status_code != 200:
96
+ logger.error(f"Error {response.status_code}: {response.reason}")
97
+ raise Exception(f"Error {response.status_code}: {response.reason}")
98
+
99
+ # If successful
100
+ try:
101
+ # Flatten test data from response
102
+ data = response.json()
103
+ # Create DataFrame from JSON
104
+ df = pd.DataFrame(data)
105
+ # Explode 'metrics' column to expand each list element into a row
106
+ df = df.explode('metrics')
107
+ # Normalize 'metrics' column
108
+ metrics_df = pd.json_normalize(df['metrics'])
109
+ # Concatenate the original DataFrame with the normalized metrics DataFrame
110
+ result_df = pd.concat([df.drop('metrics', axis=1).reset_index(drop=True), metrics_df], axis=1)
111
+
112
+ result_df.attrs['Count'] = int(len(result_df))
113
+ logger.info("Request for Metrics successful")
114
+ return result_df
115
+
116
+ except ValueError:
117
+ logger.error("Failed to parse JSON response or no data returned")
118
+ raise Exception("Failed to parse JSON response or no data returned.")