Skip to content

Commit 8b25392

Browse files
authored
Add time zone metadata (#259)
* Add time zone metadata Different metadata structure for gt3x and bin * Bump actions checkout * PR review modifications Added IOError catch into readers Remove GMT from timezone string * Change to ValueError for bad extension
1 parent 3dbf583 commit 8b25392

8 files changed

Lines changed: 40 additions & 10 deletions

File tree

.github/workflows/docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
build-and-publish-docs:
1616
runs-on: ubuntu-latest
1717
steps:
18-
- uses: actions/checkout@v4
18+
- uses: actions/checkout@v5
1919
- name: Fetch LFS files
2020
run: git lfs pull
2121
- uses: actions/setup-python@v5

.github/workflows/pypi.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
runs-on: ubuntu-latest
1818

1919
steps:
20-
- uses: actions/checkout@v4
20+
- uses: actions/checkout@v5
2121
with:
2222
fetch-depth: ${{ github.event_name == 'pull_request' && 2 || 0 }}
2323
- uses: actions/setup-python@v5

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
runs-on: ubuntu-latest
1818
steps:
1919
- name: Check out the repo
20-
uses: actions/checkout@v4
20+
uses: actions/checkout@v5
2121
with:
2222
lfs: true
2323

.github/workflows/test.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
python_version: ['3.10', '3.11', '3.12', '3.13']
2222
resolution: [lowest-direct, highest]
2323
steps:
24-
- uses: actions/checkout@v4
24+
- uses: actions/checkout@v5
2525
- name: Install poetry
2626
run: pipx install poetry
2727

@@ -57,7 +57,7 @@ jobs:
5757
ruff:
5858
runs-on: ubuntu-latest
5959
steps:
60-
- uses: actions/checkout@v4
60+
- uses: actions/checkout@v5
6161
- name: Install poetry
6262
run: pipx install poetry
6363
- uses: actions/setup-python@v5
@@ -75,7 +75,7 @@ jobs:
7575
mypy:
7676
runs-on: ubuntu-latest
7777
steps:
78-
- uses: actions/checkout@v4
78+
- uses: actions/checkout@v5
7979
- name: Install poetry
8080
run: pipx install poetry
8181
- uses: actions/setup-python@v5

src/wristpy/core/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class WatchData(BaseModel):
118118
temperature: Optional[Measurement] = None
119119
idle_sleep_mode_flag: Optional[bool] = None
120120
dynamic_range: Optional[tuple[float, float]] = None
121+
time_zone: Optional[str] = None
121122

122123
@field_validator("acceleration")
123124
def validate_acceleration(cls, v: Measurement) -> Measurement:

src/wristpy/core/orchestrator.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ def _run_file(
419419
"activity_metric": activity_metric,
420420
"nonwear_algorithm": list(nonwear_algorithm),
421421
"input_file": str(input),
422+
"time_zone": watch_data.time_zone,
422423
}
423424

424425
results = writers.OrchestratorResults(

src/wristpy/io/readers/readers.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,13 @@ def read_watch_data(file_name: Union[pathlib.Path, str]) -> models.WatchData:
2323
Returns:
2424
WatchData class
2525
26-
Raises: IOError if the file extension is not supported or doesn't exist.
26+
Raises:
27+
ValueError if the file extension is not supported.
28+
IOError if the file cannot be read using actfast.
2729
"""
30+
file_type = pathlib.Path(file_name).suffix
31+
if file_type not in (".gt3x", ".bin"):
32+
raise ValueError(f"File type {file_type} is not supported.")
2833
try:
2934
data = actfast.read(file_name)
3035
except Exception as e:
@@ -39,12 +44,14 @@ def read_watch_data(file_name: Union[pathlib.Path, str]) -> models.WatchData:
3944
measurements=sensor_values, time=time
4045
)
4146

42-
file_type = pathlib.Path(file_name).suffix
4347
idle_sleep_mode_flag = False
4448
if file_type == ".gt3x":
4549
idle_sleep_mode_flag = (
4650
data["metadata"]["device_feature_enabled"]["sleep_mode"].lower() == "true"
4751
)
52+
time_zone = data["metadata"]["info"]["TimeZone"]
53+
elif file_type == ".bin":
54+
time_zone = data["metadata"]["Configuration Info"]["Time Zone"][4:]
4855

4956
dynamic_range = _extract_dynamic_range(
5057
metadata=data["metadata"],
@@ -59,6 +66,7 @@ def read_watch_data(file_name: Union[pathlib.Path, str]) -> models.WatchData:
5966
temperature=measurements.get("temperature"),
6067
idle_sleep_mode_flag=idle_sleep_mode_flag,
6168
dynamic_range=dynamic_range,
69+
time_zone=str(time_zone),
6270
)
6371

6472

tests/unit/test_readers.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
def test_read_invalid_extenstion(sample_data_txt: pathlib.Path) -> None:
1414
"""Test the read_watch_data function with an invalid file extension."""
15-
with pytest.raises(IOError):
15+
with pytest.raises(ValueError, match="File type .txt is not supported."):
1616
readers.read_watch_data(sample_data_txt)
1717

1818

@@ -42,7 +42,7 @@ def test_geneactiv_bin_loader(sample_data_bin: pathlib.Path) -> None:
4242

4343
def test_nonexistent_file() -> None:
4444
"""Test the correct error is raised for nonexistent file."""
45-
with pytest.raises(IOError):
45+
with pytest.raises(IOError, match="Error reading file:"):
4646
readers.read_watch_data("nonexistent_file.gt3x")
4747

4848

@@ -74,3 +74,23 @@ def test_extract_dynamic_range_gt3x(sample_data_gt3x: pathlib.Path) -> None:
7474
assert (
7575
result == expected_dynamic_range
7676
), f"Expected dynamic range of: {expected_dynamic_range}, result was: {result}"
77+
78+
79+
def test_timezone_extraction_gt3x(sample_data_gt3x: pathlib.Path) -> None:
80+
"""Test extracting timezone metadata from .gt3x files."""
81+
expected_timezone = "-05:00:00"
82+
watch_data = readers.read_watch_data(sample_data_gt3x)
83+
84+
assert (
85+
watch_data.time_zone == expected_timezone
86+
), f"Expected timezone of: {expected_timezone}, result was: {watch_data.time_zone}"
87+
88+
89+
def test_timezone_extraction_bin(sample_data_bin: pathlib.Path) -> None:
90+
"""Test extracting timezone metadata from .bin files."""
91+
expected_timezone = "-05:00"
92+
watch_data = readers.read_watch_data(sample_data_bin)
93+
94+
assert (
95+
watch_data.time_zone == expected_timezone
96+
), f"Expected timezone of: {expected_timezone}, result was: {watch_data.time_zone}"

0 commit comments

Comments
 (0)