Skip to content

Commit 0c66b77

Browse files
Dev 580 hdforce v1.1.3 test metrics and forcetime (#10)
* Updates to BuildDB, SyncDB, from and to date format, and data frame output headers * updated GetForceTime and docs pages * update to pytest scripts and github actions workflow * update test files * Changes to utils.py to refine header name cleaning to match MetricDictionary and universal metric naming convention. * Remove local data management and maintain minor patch improvements * updated init file to correct debugging error * Update to AuthManager and utils for orgName inclusion * fix to lock file conflict * correction to lock file * Update README.md Co-authored-by: Andrew Wales <158098925+awales-hd@users.noreply.github.com> * updated recommendations and fixes --------- Co-authored-by: Andrew Wales <158098925+awales-hd@users.noreply.github.com>
1 parent ff4dab7 commit 0c66b77

32 files changed

Lines changed: 1995 additions & 1626 deletions

.github/workflows/push-test.yml

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This workflow will install Python dependencies,
22
# run tests and lint with Python versions 3.9<>3.12
33

4-
name: Test Package and TestPyPI
4+
name: Test Package on Push
55
on:
66
push:
77
branches:
@@ -34,7 +34,7 @@ jobs:
3434
with:
3535
python-version: ${{ matrix.python-version }}
3636

37-
- name: cache dependecies
37+
- name: cache dependencies
3838
uses: actions/cache@v4
3939
id: cache
4040
with:
@@ -56,21 +56,21 @@ jobs:
5656
- name: Run tests with pytest
5757
env:
5858
HD_REFRESH_TOKEN: ${{ secrets.HD_REFRESH_TOKEN }}
59-
run: poetry run pytest tests/
59+
run: poetry run pytest tests/ -v
6060

6161
# Job 2: Publish to Test PyPI
62-
publish-test-package:
63-
name: Publish to TestPyPI
64-
runs-on: ubuntu-latest
65-
needs: test-package
62+
#publish-test-package:
63+
# name: Publish to TestPyPI
64+
# runs-on: ubuntu-latest
65+
# needs: test-package
6666

67-
steps:
68-
- name: Checkout Code
69-
uses: actions/checkout@v4
67+
# steps:
68+
# - name: Checkout Code
69+
# uses: actions/checkout@v4
7070

71-
- name: Build and publish to pypi
72-
uses: JRubics/poetry-publish@v2.0
73-
with:
74-
pypi_token: ${{ secrets.TEST_PYPI_TOKEN }}
75-
repository_name: "testpypi"
76-
repository_url: "https://test.pypi.org/legacy/"
71+
# - name: Build and publish to pypi
72+
# uses: JRubics/poetry-publish@v2.0
73+
# with:
74+
# pypi_token: ${{ secrets.TEST_PYPI_TOKEN }}
75+
# repository_name: "testpypi"
76+
# repository_url: "https://test.pypi.org/legacy/"

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
"changedate",
44
"datetime",
55
"hdlogo",
6-
"mytests"
6+
"mytests",
7+
"pyjanitor"
78
],
89
"python.testing.pytestArgs": [
910
"tests"

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 Hawkin Dynamics
3+
Copyright (c) 2025 Hawkin Dynamics
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# HDFORCE v1.1.2 <img src="docs/img/hdlogo_sm.png" align="right" alt="" width="120" />
1+
# HDFORCE v1.1.3 <img src="docs/img/hdlogo_sm.png" align="right" alt="" width="120" />
22

33

44
**Get your data from the Hawkin Dynamics API**
@@ -121,6 +121,7 @@ types
121121
# Get Athletes
122122
roster = hdforce.GetAthletes( includeInactive= False) # includeInactive defaults to False
123123

124+
124125
# Athlete example
125126
roster[roster['name'] =="Lauren Green"]
126127
```

docs/About/changelog.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelogs
22

3+
## hdforce v1.1.3
4+
5+
* Addition of `last_sync_time` value to `GetTests` functions
6+
7+
* Changes to `GetTests` metric headers. Improved consistency of naming reflective of the metric library
8+
9+
* Improved functionality to `GetTests` to accept character strings in the format "YYYY-MM-DD" for `from` and `to`
10+
11+
* Addition of 'Metric Library' returned from `GetMetrics`
12+
13+
* Improved functionality to `GetForceTime`to return all test types
14+
315
## hdforce v1.1.2
416

517
* Bug fix: addition of new TruStrength test names and IDs to testTypeId validation method

docs/About/license.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# MIT License
22

3-
Copyright (c) 2024 Hawkin Dynamics
3+
Copyright (c) 2025 Hawkin Dynamics
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

docs/Functions/GetMetrics.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ A Pandas DataFrame containing the information for the metrics of each test type,
1010
* __testTypeName__: The name of each metric.
1111
* __id__: The unique identifier for each metric.
1212
* __label__: The label (common name) for each metric
13+
* __label_unit__: The headers of the metrics as they are returned from the API
1314
* __units__: Units of measure
1415
* __description__: Full description of metric and calculation*
1516

@@ -34,10 +35,10 @@ print(metrics[10:15])
3435

3536
_output_
3637

37-
| canonicalTestTypeId | testTypeName | id | label | units | description |
38-
|---------------------|---------------------|---------------------------|--------------------------------|-------|---------------------------------------------------------------------------------------------------------------------------------------------|
39-
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakBrakingForce | Peak Braking Force | N | The peak instantaneous vertical ground reaction force applied to the system center of mass during the braking phase. |
40-
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakRelativeBrakingForce | Peak Relative Braking Force | % | The peak instantaneous vertical ground reaction force applied to the system center of mass during the braking phase as a percentage of system weight. |
41-
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | avgPropulsiveForce | Avg. Propulsive Force | N | The average vertical ground reaction force applied to the system center of mass during the propulsion phase. |
42-
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | avgRelativePropulsiveForce| Avg. Relative Propulsive Force | % | The average vertical ground reaction force applied to the system center of mass during the propulsion phase as a percentage of system weight. |
43-
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakPropulsiveForce | Peak Propulsive Force | N | The peak instantaneous vertical ground reaction force applied to the system center of mass during the propulsion phase. |
38+
| canonicalTestTypeId | testTypeName | id | label | label_unit | units | description |
39+
|----------------------|----------------------|---------------------------|--------------------------------|------------|-------|---------------------------------------------------------------------------------------------------------------------------------------------|
40+
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakBrakingForce | Peak Braking Force | Peak Braking Force(N) | N | The peak instantaneous vertical ground reaction force applied to the system center of mass during the braking phase. |
41+
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakRelativeBrakingForce | Peak Relative Braking Force | Peak Relative Braking Force(%) | % | The peak instantaneous vertical ground reaction force applied to the system center of mass during the braking phase as a percentage of system weight. |
42+
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | avgPropulsiveForce | Avg. Propulsive Force | Avg. Propulsive Force(N) | N | The average vertical ground reaction force applied to the system center of mass during the propulsion phase. |
43+
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | avgRelativePropulsiveForce| Avg. Relative Propulsive Force | Avg. Relative Propulsive Force(%) | % | The average vertical ground reaction force applied to the system center of mass during the propulsion phase as a percentage of system weight. |
44+
| 7nNduHeM5zETPjHxvm7s | Countermovement Jump | peakPropulsiveForce | Peak Propulsive Force | Peak Propulsive Force(N) |N | The peak instantaneous vertical ground reaction force applied to the system center of mass during the propulsion phase. |

docs/Functions/GetTests.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ __`GetTests(from_: int = None, to_: int = None, sync: bool = False, athleteId: s
44
Get all test trials from an account. Allows filtering of results based on time frames, synchronization needs, and the active status of tests.
55

66
### Parameters
7-
__`from_`__: _(int)_ Unix timestamp specifying the start time from which tests should be fetched. Default is None, which fetches tests from the beginning.
7+
__`from_`__: _(int | str)_ Unix timestamp or string date (e.g., "YYYY-MM-DD") specifying the start time from which tests should be fetched. Default is None, which fetches tests from the beginning.
88

9-
__`to_`__: _(int)_ Unix timestamp specifying the end time until which tests should be fetched. Default is None, which fetches tests up to the current time.
9+
__`to_`__: _(int | str)_ Unix timestamp or string date (e.g., "YYYY-MM-DD") specifying the end time until which tests should be fetched. Default is None, which fetches tests up to the current time.
1010

1111
__`sync`__: _(bool)_ If True, the function fetches updated and newly created tests to synchronize with the database. Default is False.
1212

hdforce/AuthManager.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
# Authenticator
1212

1313

14-
def AuthManager(authMethod: str = "env", refreshToken_name: str = "HD_REFRESH_TOKEN", refreshToken: str = None, env_file_name: str = None, region: str = "Americas") -> None:
14+
def AuthManager(authMethod: str = "env", refreshToken_name: str = "HD_REFRESH_TOKEN", refreshToken: str = None, env_file_name: str = None, region: str = "Americas", orgName: str = None) -> None:
1515
""" Choose the authentication settings
1616
1717
@@ -32,6 +32,8 @@ def AuthManager(authMethod: str = "env", refreshToken_name: str = "HD_REFRESH_TO
3232
env_file_name : str
3333
Required with authMethod='file'. Provides file name for variable storage.
3434
35+
orgName : str, optional
36+
If provided, it modifies the cloud API endpoint to access organization-specific resources.
3537
3638
Raises
3739
------
@@ -75,12 +77,11 @@ def AuthManager(authMethod: str = "env", refreshToken_name: str = "HD_REFRESH_TO
7577
logger.debug(f"Refresh token: {kabrv}xxxx method: {authMethod}")
7678

7779
# Setup session assuming TokenManager handles authentication
78-
session = TokenManager(refreshToken=key, region=region, fileName=env_file_name)
80+
session = TokenManager(refreshToken=key, region=region, orgName=orgName, fileName=env_file_name)
7981

8082
# Create objects of classes
8183
accessToken = str(session.accessToken)
8284
tokenExpiration = str(session.ExpirationVal)
83-
expirationStr = str(session.ExpirationStr)
8485
cloudURL = str(session.url_cloud)
8586
fileName = str(session.fileName)
8687

hdforce/GetForceTime.py

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -110,25 +110,58 @@ def GetForceTime(testId: str) -> pd.DataFrame:
110110
# Flatten test data from response
111111
data = response.json()
112112

113+
# Get Test Type
114+
test_type = data['testType']['canonicalId']
115+
116+
def pad_array(arr, target_length, pad_value=None):
117+
return arr + [pad_value] * (target_length - len(arr))
118+
119+
# Target length is the length of the primary time array
120+
target_length = len(data.get("Time(s)", []))
121+
122+
time_data = data.get("Time(s)", [])
123+
left_force = pad_array(data.get("LeftForce(N)", []), target_length, None)
124+
right_force = pad_array(data.get("RightForce(N)", []), target_length, None)
125+
combined_force = pad_array(data.get("CombinedForce(N)", []), target_length, None)
126+
velocity = pad_array(data.get("Velocity(m/s)", []), target_length, None)
127+
displacement = pad_array(data.get("Displacement(m)", []), target_length, None)
128+
power = pad_array(data.get("Power(W)", []), target_length, None)
129+
113130
# Create DataFrame from the array data
114-
df = pd.DataFrame({
115-
"Time(s)": data["Time(s)"],
116-
"LeftForce(N)": data["LeftForce(N)"],
117-
"RightForce(N)": data["RightForce(N)"],
118-
"CombinedForce(N)": data["CombinedForce(N)"],
119-
"Velocity(m/s)": data["Velocity(m/s)"],
120-
"Displacement(m)": data["Displacement(m)"],
121-
"Power(W)": data["Power(W)"],
122-
"rsi": [data["rsi"]] * len(data["Time(s)"]) # Assuming rsi is a constant value
123-
})
131+
if test_type in [
132+
"r4fhrkPdYlLxYQxEeM78", # Multi Rebound
133+
"2uS5XD5kXmWgIZ5HhQ3A", # Isometric
134+
"5pRSUQVSJVnxijpPMck3", # Free Run
135+
"ubeWMPN1lJFbuQbAM97s" # Weigh In
136+
]:
137+
df = pd.DataFrame({
138+
"time": time_data,
139+
"leftForce": left_force,
140+
"rightForce": right_force,
141+
"combinedForce": combined_force
142+
})
143+
elif test_type in ["4KlQgKmBxbOY6uKTLDFL", "umnEZPgi6zaxuw0KhUpM"]: # TruStrength tests
144+
df = pd.DataFrame({
145+
"time": time_data,
146+
"combinedForce": combined_force
147+
})
148+
else:
149+
df = pd.DataFrame({
150+
"time": time_data,
151+
"leftForce": left_force,
152+
"rightForce": right_force,
153+
"combinedForce": combined_force,
154+
"velocity": velocity,
155+
"displacement": displacement,
156+
"power": power
157+
})
124158

125159
# Setting attributes
126160
df.attrs['Test ID'] = data['id']
127161
df.attrs['Test Name'] = data['testType']['name']
128162
df.attrs['Athlete Name'] = data['athlete']['name']
129163
df.attrs['Athlete ID'] = data['athlete']['id']
130164
df.attrs['Timestamp'] = pd.to_datetime(data['timestamp'], unit='s')
131-
df.attrs['RSI'] = data['rsi']
132165
logger.info(f"Request successful: {df.attrs['Test Name']} - {df.attrs['Test ID']} - {df.attrs['Timestamp']}")
133166
return df
134167

0 commit comments

Comments
 (0)