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 +170 -0
- hdforce/GetAthletes.py +124 -0
- hdforce/GetForceTime.py +140 -0
- hdforce/GetGroups.py +107 -0
- hdforce/GetMetrics.py +118 -0
- hdforce/GetTags.py +108 -0
- hdforce/GetTeams.py +108 -0
- hdforce/GetTests.py +158 -0
- hdforce/GetTestsAth.py +176 -0
- hdforce/GetTestsGroup.py +170 -0
- hdforce/GetTestsTeam.py +162 -0
- hdforce/GetTestsType.py +190 -0
- hdforce/GetTypes.py +106 -0
- hdforce/LoggerConfig.py +51 -0
- hdforce/__init__.py +21 -0
- hdforce/utils.py +306 -0
- hdforce-1.0.0.dist-info/LICENSE.txt +21 -0
- hdforce-1.0.0.dist-info/METADATA +173 -0
- hdforce-1.0.0.dist-info/RECORD +20 -0
- hdforce-1.0.0.dist-info/WHEEL +4 -0
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.")
|
hdforce/GetForceTime.py
ADDED
|
@@ -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.")
|