hdforce 1.0.0__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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Hawkin Dynamics
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
hdforce-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,173 @@
1
+ Metadata-Version: 2.1
2
+ Name: hdforce
3
+ Version: 1.0.0
4
+ Summary: Get your data from the Hawkin Dynamics API
5
+ Home-page: https://www.hawkindynamics.com/
6
+ Keywords: hawkin,force plates,sports science
7
+ Author: laureng-hd
8
+ Author-email: lauren@hawkindynamics.com
9
+ Requires-Python: >=3.9,<4.0
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Build Tools
19
+ Requires-Dist: flake8 (>=7.0.0,<8.0.0)
20
+ Requires-Dist: pandas (>=2.2.0,<3.0.0)
21
+ Requires-Dist: pytest (>=8.2.0,<9.0.0)
22
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
23
+ Requires-Dist: requests (>=2.31.0,<3.0.0)
24
+ Project-URL: Documentation, https://silver-adventure-22j19qq.pages.github.io/
25
+ Project-URL: Repository, https://github.com/HawkinDynamics/hawkinPy
26
+ Description-Content-Type: text/markdown
27
+
28
+ # HDFORCE <img src="docs/img/hdlogo_sm.png" align="right" alt="" width="120" />
29
+
30
+ **Get your data from the Hawkin Dynamics API**
31
+
32
+ <!-- badges: start -->
33
+ ![GitHub Release](https://img.shields.io/github/v/release/HawkinDynamics/hawkinPy)
34
+ [![Test Py Versions and OS](https://github.com/HawkinDynamics/hawkinPy/actions/workflows/push-test.yml/badge.svg?branch=dev_env)](https://github.com/HawkinDynamics/hawkinPy/actions/workflows/push-test.yml)
35
+ ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/HawkinDynamics/hawkinPy/dev_env)
36
+ [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
37
+ [![lifecycle](https://img.shields.io/badge/lifecycle-stable-green.svg)](https://www.tidyverse.org/lifecycle/#stable)
38
+ [![license](https://img.shields.io/badge/license-MIT%20+%20file%20LICENSE-lightgrey.svg)](https://choosealicense.com/)
39
+ <!-- badges: end -->
40
+
41
+ ## How To Use The HDFORCE Package
42
+
43
+ HDFORCE provides simple functionality with Hawkin Dynamics API. These functions are for use with ‘Hawkin Dynamics Beta API’ version 1.10-beta. You must be a Hawkin Dynamics user with an active integration account to utilize functions within the package.
44
+
45
+ ## Functions
46
+ This API is designed to get data out of your Hawkin Dynamics server and interact with your data more intimately. It is not designed to be accessed from client applications directly. There is a limit on the amount of data that can be returned in a single request (256 MB). As your database grows, it will be necessary to use the `from` and `to` parameters to limit the size of the responses. Responses that exceed the memory limit will timeout and fail. It is advised that you design your client to handle this from the beginning. A recommended pattern would be to have two methods of fetching data. A scheduled pull that uses the `from` and `to` parameters to constrain the returned data to only tests that have occurred since the last fetch e.g. every day or every 5 minutes. And then a pull that fetches the entire database since you began testing that is only executed when necessary. A recommended way of doing this is to generate the `from` and `to` parameters for each month since you started and send a request for each either in parallel or sequentially.
47
+
48
+ This package was meant to help execute requests to the Hawkin Dynamics API with a single line of code. There are 13 functions to help execute 4 primary objectives:
49
+
50
+ ### Authentication
51
+ Use the Refresh Token generated by https://cloud.hawkindynamics.com/integrations to get a valid Access Token. Only the organization administrator account can generate API tokens. Use this function to initiate access to your data in the cloud. All other HDFORCE functions will depend on the values returned from this function.
52
+
53
+ In HDFORCE, we can create and manage your authentication settings with the `authManager` function. The arguments passed will be used to handle authentication for the other functions used in the program. With this function, you can control these settings:
54
+
55
+ * __region__: Sets the URL prefix of your cloud site.
56
+ * __authMethod__: Sets storage method of auth variables. One of env, file, or manual. Default to 'env'.
57
+ * __refreshToken_name__: Provide or set the name of the refresh Token variable. Default to "HD_REFRESH_TOKEN".
58
+ * __refreshToken__: Optionally provided to pass a token for authentication when authMethod="manual". Else it will set the refresh token value when provided.
59
+ * __env_file_name__: Required when authManager="file". Directs storage location of variables.
60
+
61
+ The authentication manager allows the user to manage authentication with various development strategies. The 'env' method was designed to allow for simple authentication while developing in a local or contained environment, where the refresh token and variables are stored in the system environment. The 'file' method can be used for more visible and dynamic interaction with the auth variables, as the refresh token and variables are stored with a .env file. While the 'manual' method allows for the use of the package without storing sensitive data, in so the refresh token is not stored and is only used for authentication. This could be helpful in the scenario of developing a password-protected application where the token can be used as a password.
62
+
63
+ In the case of the "file" method, the user will need to save a .env file in the source directory. When using this method, you will also need to pass the file name to the `env_file_name` argument as a string. It's most common to use something as simple as ".env", but you can name your file anything ending with .env.
64
+
65
+ Below is an example of what the .env file can look like. This can be used as a template, as the variable name "HD_REFRESH_TOKEN" is the default to what the `authManager` function will use for the `refreshToken_name`. If you want to use a different variable name, simply pass the string through the `refreshToken_name` argument, along with the token to the `refreshToken` argument. The other values for ACCESS_TOKEN, TOKEN_EXPIRATION, and CLOUD_URL are defaults within the `authManager` function and should be maintained.
66
+
67
+ ``` PowerShell
68
+ # Inside your .env file
69
+
70
+ HD_REFRESH_TOKEN=your_api_token # Replace With Your Refresh Token
71
+ REGION=your_region # Replace With Your URL region
72
+ ACCESS_TOKEN= # Access token will be stored here
73
+ TOKEN_EXPIRATION= # Token Expiration will be stored here
74
+ CLOUD_URL= # Your region's URL here
75
+ ```
76
+
77
+ With the variables stored, we can use them with our other functions without worrying about authentication for each call.
78
+
79
+ ``` Python
80
+ # Run authentication manager
81
+ AuthManager(authMethod="env", region= "Americas")
82
+
83
+ # Run authentication manager with file method
84
+ AuthManager(authMethod="file", env_file_name=".env", region= "Americas")
85
+
86
+ # Run authentication manager with manual method
87
+ AuthManager(authMethod="manual", region="Americas", refreshToken="your_refresh_token")
88
+
89
+ ```
90
+
91
+ ### Hawkin Specific data
92
+ While the purpose of the package is to help with accessing data specific to your organization, it may be helpful to store some data that is specific to Hawkin and the Hawkin Dynamics system. Things like test types and test metrics will help you call your data more efficiently and better understand the values you are seeing.
93
+
94
+ * `GetTypes` - Get the test type names and IDs for all the test types in the system. The response will be a data frame containing the tests that are in the HD system.
95
+ * `GetMetrics` - Get all the metrics for each test type. The response will be a data frame containing the test canonical ID, test type name, metric ID, metric label, a metric unit of measure, and description.
96
+
97
+ ### Organization Specific Data
98
+ Every organization has data specific to them. With that, these entities will have unique IDs. It is important to have these IDs available to make the most of your test calls.
99
+
100
+ * `GetAthletes()` - Get the athletes for an account. Inactive players will only be included if `inactive` = True. The response will be a data frame containing the athletes that match this query.
101
+ * `GetTeams()` - Get the team names and IDs for all the teams in the org. The response will be a data frame containing the teams that are in the organization.
102
+ * `GetGroups()` - Get the group names and IDs for all the groups in the org. The response will be a data frame containing the groups that are in the organization.
103
+ * `GetTags()` - Get the tag names, IDs, and descriptions for tags created by users in your org. The response will be a data frame.
104
+
105
+ ### Get Test Data
106
+ This is what you are here for. These functions allow you to call test data most efficiently. It is encouraged that you take advantage of the `from_`, `to_`, and `sync` parameters. This will help prevent from having calls with large payloads that may timeout and fail.
107
+
108
+ #### Get Test Arguments
109
+ * `from_` = Optionally supply a time (Unix timestamp) you want the tests from. If you do not supply this
110
+ value you will receive every test. This parameter is best suited for bulk exports of historical data.
111
+ * `to_` = Optionally supply a time (Unix timestamp) you want the tests to. If you do not supply this value
112
+ you will receive every test from the beginning of time or the optionally supplied `from_`
113
+ parameter. This parameter is best suited for bulk exports of historical data
114
+ * `sync` = The result set will include updated and newly created tests, following the time constraints of `from_` and `to_`. This parameter is best suited to keep your database in sync with the Hawkin database. It cannot and should not be used to fetch your entire database. A recommended strategy would be to have a job that runs on a short interval e.g. every five minutes that sends the `lastSyncTime` that it received as the `from_` parameter with `sync=True`.
115
+ * `active` = If True, only active tests are fetched. If False, all tests including inactive ones are fetched. The default is set to True.
116
+
117
+ #### Get Test Function
118
+ * `GetTests()` - Get the tests for your account. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). The Response will be a data frame containing the trials within the time range (if specified).
119
+ * `GetTestsAth()` - Get only tests of the specified athlete from your organization. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from the athlete, within the time range (if specified).
120
+ * `GetTestsType()` - Get only tests of the specified test type from your organization. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from that test type, within the time range (if specified).
121
+ * `GetTestsTeam()` - Get only tests of the specified teams from your organization. Requires a `teamId` argument, which expects a text string, list or tuple (max of 10 teams). You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from those teams, within the time range (if specified).
122
+ * `GetTestsGroup()` - Get only tests of the specified groups from your organization. Requires a `groupId` argument, which expects a text string, list or tuple (max of 10 groups). You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from those groups, within the time range (if specified).
123
+ * `GetForceTime()` - Get the force-time data for a specific test by id. This includes both left, right and combined force data at 1000hz (per millisecond). Calculated velocity, displacement, and power at each time interval will also be included.
124
+
125
+ ## Examples
126
+ This is a basic example that shows a common workflow:
127
+
128
+ ### Authenticate Session
129
+ ``` Python
130
+ # Dependencies
131
+ import hdforce
132
+ # Run authentication manager
133
+ AuthManager(authMethod="env", region= "Americas")
134
+ ```
135
+ ### Get HD Data
136
+ #### Test Types
137
+ ``` Python
138
+ # Test Types
139
+ types = hdforce.GetTypes()
140
+
141
+ types
142
+ ```
143
+
144
+ ### Organization Data
145
+ #### Get Athletes
146
+ ``` Python
147
+ # Get Athletes
148
+ roster = hdforce.GetAthletes( inactive= False) # inactive is default to False
149
+
150
+ # Athlete example
151
+ roster[roster['name'] =="Lauren Green"]
152
+ ```
153
+
154
+ #### Get Tags
155
+ ``` Python
156
+ # Get Tags
157
+ tags = hdforce.GetTags()
158
+
159
+ # Tags example
160
+ tags[0:6]
161
+ ```
162
+
163
+ ### Get Tests
164
+ ``` Python
165
+ # Get Tags
166
+ allTests = hdforce.GetTests()
167
+ # Filter my tests
168
+ mytests = allTests[allTests['athlete.name'] =="Lauren Green"]
169
+ # Slice some rows and columns
170
+ df = mytests.iloc[:5,:15]
171
+
172
+ df
173
+ ```
@@ -0,0 +1,146 @@
1
+ # HDFORCE <img src="docs/img/hdlogo_sm.png" align="right" alt="" width="120" />
2
+
3
+ **Get your data from the Hawkin Dynamics API**
4
+
5
+ <!-- badges: start -->
6
+ ![GitHub Release](https://img.shields.io/github/v/release/HawkinDynamics/hawkinPy)
7
+ [![Test Py Versions and OS](https://github.com/HawkinDynamics/hawkinPy/actions/workflows/push-test.yml/badge.svg?branch=dev_env)](https://github.com/HawkinDynamics/hawkinPy/actions/workflows/push-test.yml)
8
+ ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/HawkinDynamics/hawkinPy/dev_env)
9
+ [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
10
+ [![lifecycle](https://img.shields.io/badge/lifecycle-stable-green.svg)](https://www.tidyverse.org/lifecycle/#stable)
11
+ [![license](https://img.shields.io/badge/license-MIT%20+%20file%20LICENSE-lightgrey.svg)](https://choosealicense.com/)
12
+ <!-- badges: end -->
13
+
14
+ ## How To Use The HDFORCE Package
15
+
16
+ HDFORCE provides simple functionality with Hawkin Dynamics API. These functions are for use with ‘Hawkin Dynamics Beta API’ version 1.10-beta. You must be a Hawkin Dynamics user with an active integration account to utilize functions within the package.
17
+
18
+ ## Functions
19
+ This API is designed to get data out of your Hawkin Dynamics server and interact with your data more intimately. It is not designed to be accessed from client applications directly. There is a limit on the amount of data that can be returned in a single request (256 MB). As your database grows, it will be necessary to use the `from` and `to` parameters to limit the size of the responses. Responses that exceed the memory limit will timeout and fail. It is advised that you design your client to handle this from the beginning. A recommended pattern would be to have two methods of fetching data. A scheduled pull that uses the `from` and `to` parameters to constrain the returned data to only tests that have occurred since the last fetch e.g. every day or every 5 minutes. And then a pull that fetches the entire database since you began testing that is only executed when necessary. A recommended way of doing this is to generate the `from` and `to` parameters for each month since you started and send a request for each either in parallel or sequentially.
20
+
21
+ This package was meant to help execute requests to the Hawkin Dynamics API with a single line of code. There are 13 functions to help execute 4 primary objectives:
22
+
23
+ ### Authentication
24
+ Use the Refresh Token generated by https://cloud.hawkindynamics.com/integrations to get a valid Access Token. Only the organization administrator account can generate API tokens. Use this function to initiate access to your data in the cloud. All other HDFORCE functions will depend on the values returned from this function.
25
+
26
+ In HDFORCE, we can create and manage your authentication settings with the `authManager` function. The arguments passed will be used to handle authentication for the other functions used in the program. With this function, you can control these settings:
27
+
28
+ * __region__: Sets the URL prefix of your cloud site.
29
+ * __authMethod__: Sets storage method of auth variables. One of env, file, or manual. Default to 'env'.
30
+ * __refreshToken_name__: Provide or set the name of the refresh Token variable. Default to "HD_REFRESH_TOKEN".
31
+ * __refreshToken__: Optionally provided to pass a token for authentication when authMethod="manual". Else it will set the refresh token value when provided.
32
+ * __env_file_name__: Required when authManager="file". Directs storage location of variables.
33
+
34
+ The authentication manager allows the user to manage authentication with various development strategies. The 'env' method was designed to allow for simple authentication while developing in a local or contained environment, where the refresh token and variables are stored in the system environment. The 'file' method can be used for more visible and dynamic interaction with the auth variables, as the refresh token and variables are stored with a .env file. While the 'manual' method allows for the use of the package without storing sensitive data, in so the refresh token is not stored and is only used for authentication. This could be helpful in the scenario of developing a password-protected application where the token can be used as a password.
35
+
36
+ In the case of the "file" method, the user will need to save a .env file in the source directory. When using this method, you will also need to pass the file name to the `env_file_name` argument as a string. It's most common to use something as simple as ".env", but you can name your file anything ending with .env.
37
+
38
+ Below is an example of what the .env file can look like. This can be used as a template, as the variable name "HD_REFRESH_TOKEN" is the default to what the `authManager` function will use for the `refreshToken_name`. If you want to use a different variable name, simply pass the string through the `refreshToken_name` argument, along with the token to the `refreshToken` argument. The other values for ACCESS_TOKEN, TOKEN_EXPIRATION, and CLOUD_URL are defaults within the `authManager` function and should be maintained.
39
+
40
+ ``` PowerShell
41
+ # Inside your .env file
42
+
43
+ HD_REFRESH_TOKEN=your_api_token # Replace With Your Refresh Token
44
+ REGION=your_region # Replace With Your URL region
45
+ ACCESS_TOKEN= # Access token will be stored here
46
+ TOKEN_EXPIRATION= # Token Expiration will be stored here
47
+ CLOUD_URL= # Your region's URL here
48
+ ```
49
+
50
+ With the variables stored, we can use them with our other functions without worrying about authentication for each call.
51
+
52
+ ``` Python
53
+ # Run authentication manager
54
+ AuthManager(authMethod="env", region= "Americas")
55
+
56
+ # Run authentication manager with file method
57
+ AuthManager(authMethod="file", env_file_name=".env", region= "Americas")
58
+
59
+ # Run authentication manager with manual method
60
+ AuthManager(authMethod="manual", region="Americas", refreshToken="your_refresh_token")
61
+
62
+ ```
63
+
64
+ ### Hawkin Specific data
65
+ While the purpose of the package is to help with accessing data specific to your organization, it may be helpful to store some data that is specific to Hawkin and the Hawkin Dynamics system. Things like test types and test metrics will help you call your data more efficiently and better understand the values you are seeing.
66
+
67
+ * `GetTypes` - Get the test type names and IDs for all the test types in the system. The response will be a data frame containing the tests that are in the HD system.
68
+ * `GetMetrics` - Get all the metrics for each test type. The response will be a data frame containing the test canonical ID, test type name, metric ID, metric label, a metric unit of measure, and description.
69
+
70
+ ### Organization Specific Data
71
+ Every organization has data specific to them. With that, these entities will have unique IDs. It is important to have these IDs available to make the most of your test calls.
72
+
73
+ * `GetAthletes()` - Get the athletes for an account. Inactive players will only be included if `inactive` = True. The response will be a data frame containing the athletes that match this query.
74
+ * `GetTeams()` - Get the team names and IDs for all the teams in the org. The response will be a data frame containing the teams that are in the organization.
75
+ * `GetGroups()` - Get the group names and IDs for all the groups in the org. The response will be a data frame containing the groups that are in the organization.
76
+ * `GetTags()` - Get the tag names, IDs, and descriptions for tags created by users in your org. The response will be a data frame.
77
+
78
+ ### Get Test Data
79
+ This is what you are here for. These functions allow you to call test data most efficiently. It is encouraged that you take advantage of the `from_`, `to_`, and `sync` parameters. This will help prevent from having calls with large payloads that may timeout and fail.
80
+
81
+ #### Get Test Arguments
82
+ * `from_` = Optionally supply a time (Unix timestamp) you want the tests from. If you do not supply this
83
+ value you will receive every test. This parameter is best suited for bulk exports of historical data.
84
+ * `to_` = Optionally supply a time (Unix timestamp) you want the tests to. If you do not supply this value
85
+ you will receive every test from the beginning of time or the optionally supplied `from_`
86
+ parameter. This parameter is best suited for bulk exports of historical data
87
+ * `sync` = The result set will include updated and newly created tests, following the time constraints of `from_` and `to_`. This parameter is best suited to keep your database in sync with the Hawkin database. It cannot and should not be used to fetch your entire database. A recommended strategy would be to have a job that runs on a short interval e.g. every five minutes that sends the `lastSyncTime` that it received as the `from_` parameter with `sync=True`.
88
+ * `active` = If True, only active tests are fetched. If False, all tests including inactive ones are fetched. The default is set to True.
89
+
90
+ #### Get Test Function
91
+ * `GetTests()` - Get the tests for your account. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). The Response will be a data frame containing the trials within the time range (if specified).
92
+ * `GetTestsAth()` - Get only tests of the specified athlete from your organization. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from the athlete, within the time range (if specified).
93
+ * `GetTestsType()` - Get only tests of the specified test type from your organization. You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from that test type, within the time range (if specified).
94
+ * `GetTestsTeam()` - Get only tests of the specified teams from your organization. Requires a `teamId` argument, which expects a text string, list or tuple (max of 10 teams). You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from those teams, within the time range (if specified).
95
+ * `GetTestsGroup()` - Get only tests of the specified groups from your organization. Requires a `groupId` argument, which expects a text string, list or tuple (max of 10 groups). You can specify a time frame `from_`, or `to_`, which the tests should come (or be synced). Response will be a data frame containing the trials from those groups, within the time range (if specified).
96
+ * `GetForceTime()` - Get the force-time data for a specific test by id. This includes both left, right and combined force data at 1000hz (per millisecond). Calculated velocity, displacement, and power at each time interval will also be included.
97
+
98
+ ## Examples
99
+ This is a basic example that shows a common workflow:
100
+
101
+ ### Authenticate Session
102
+ ``` Python
103
+ # Dependencies
104
+ import hdforce
105
+ # Run authentication manager
106
+ AuthManager(authMethod="env", region= "Americas")
107
+ ```
108
+ ### Get HD Data
109
+ #### Test Types
110
+ ``` Python
111
+ # Test Types
112
+ types = hdforce.GetTypes()
113
+
114
+ types
115
+ ```
116
+
117
+ ### Organization Data
118
+ #### Get Athletes
119
+ ``` Python
120
+ # Get Athletes
121
+ roster = hdforce.GetAthletes( inactive= False) # inactive is default to False
122
+
123
+ # Athlete example
124
+ roster[roster['name'] =="Lauren Green"]
125
+ ```
126
+
127
+ #### Get Tags
128
+ ``` Python
129
+ # Get Tags
130
+ tags = hdforce.GetTags()
131
+
132
+ # Tags example
133
+ tags[0:6]
134
+ ```
135
+
136
+ ### Get Tests
137
+ ``` Python
138
+ # Get Tags
139
+ allTests = hdforce.GetTests()
140
+ # Filter my tests
141
+ mytests = allTests[allTests['athlete.name'] =="Lauren Green"]
142
+ # Slice some rows and columns
143
+ df = mytests.iloc[:5,:15]
144
+
145
+ df
146
+ ```
@@ -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}.")
@@ -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.")