Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hydration #61

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json # Schema for CodeRabbit configurations
Copy link

@coderabbitai coderabbitai bot Jul 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a description for the schema URL.

Adding a brief description for the schema URL can improve the readability of the configuration file.

# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json # Schema for CodeRabbit configurations

Would you like me to add this comment for better clarity?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matin, I've added the requested description to the .coderabbit.yaml file for better clarity.

# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json # Schema for CodeRabbit configurations

This should improve the readability of the configuration file.


Learnings added
Learnt from: matin
PR: matin/garth#61
File: .coderabbit.yaml:1-1
Timestamp: 2024-07-01T19:11:47.476Z
Learning: When updating the `.coderabbit.yaml` file, adding a brief description for the schema URL can improve readability and clarity.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

language: "en-US"
early_access: true
reviews:
profile: "assertive"
request_changes_workflow: false
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
auto_review:
enabled: true
drafts: false
path_filters:
- "!tests/**/cassettes/**"
path_instructions:
- path: "tests/**"
instructions: |
- test functions shouldn't have a return type hint
- it's ok to use `assert` instead of `pytest.assume()`
chat:
auto_reply: true
2 changes: 2 additions & 0 deletions garth/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .http import Client, client
from .stats import (
DailyHRV,
DailyHydration,
DailyIntensityMinutes,
DailySleep,
DailySteps,
Expand All @@ -16,6 +17,7 @@
__all__ = [
"Client",
"DailyHRV",
"DailyHydration",
"DailyIntensityMinutes",
"DailySleep",
"DailySteps",
Expand Down
2 changes: 1 addition & 1 deletion garth/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def download(self, path: str, **kwargs) -> bytes:

def upload(
self, fp: IO[bytes], /, path: str = "/upload-service/upload"
) -> Dict[str, Any]:
) -> Optional[Dict[str, Any]]:
fname = os.path.basename(fp.name)
files = {"file": (fname, fp)}
return self.connectapi(
Expand Down
2 changes: 2 additions & 0 deletions garth/stats/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = [
"DailyHRV",
"DailyHydration",
matin marked this conversation as resolved.
Show resolved Hide resolved
"DailyIntensityMinutes",
"DailySleep",
"DailySteps",
Expand All @@ -10,6 +11,7 @@
]

from .hrv import DailyHRV
from .hydration import DailyHydration
from .intensity_minutes import DailyIntensityMinutes, WeeklyIntensityMinutes
from .sleep import DailySleep
from .steps import DailySteps, WeeklySteps
Expand Down
9 changes: 3 additions & 6 deletions garth/stats/_base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
from datetime import date, timedelta
from typing import ClassVar, List, Optional, Union
from typing import ClassVar, List, Optional, Type, Union

from pydantic.dataclasses import dataclass

from .. import http
from ..utils import camel_to_snake_dict, format_end_date

BASE_PATH = "/usersummary-service/stats/stress"


@dataclass(frozen=True)
class Stats:
Expand All @@ -18,7 +16,7 @@ class Stats:

@classmethod
def list(
cls,
cls: Type["Stats"],
end: Union[date, str, None] = None,
period: int = 1,
*,
Expand All @@ -32,15 +30,14 @@ def list(
page = cls.list(end, cls._page_size, client=client)
if not page:
return []
page = (
return (
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider optimizing recursive calls.

The recursive call to cls.list can be optimized to avoid deep recursion, which might lead to stack overflow for large periods. Consider using iteration instead.

def list(
    cls: Type["Stats"],
    end: Union[date, str, None] = None,
    period: int = 1,
    *,
    client: Optional[http.Client] = None,
) -> List["Stats"]:
    client = client or http.client
    end = format_end_date(end)
    period_type = "days" if "daily" in cls._path else "weeks"
    
    results = []
    while period > cls._page_size:
        page = cls.list(end, cls._page_size, client=client)
        if not page:
            return results
        results = page + results
        end -= timedelta(**{period_type: cls._page_size})
        period -= cls._page_size

    start = end - timedelta(**{period_type: period - 1})
    path = cls._path.format(start=start, end=end, period=period)
    page_dirs = client.connectapi(path)
    if not page_dirs:
        return results
    if "values" in page_dirs[0]:
        page_dirs = [{**stat, **stat.pop("values")} for stat in page_dirs]
    page_dirs = [camel_to_snake_dict(stat) for stat in page_dirs]
    return [cls(**stat) for stat in page_dirs] + results

cls.list(
end - timedelta(**{period_type: cls._page_size}),
period - cls._page_size,
client=client,
)
+ page
)
return page

start = end - timedelta(**{period_type: period - 1})
path = cls._path.format(start=start, end=end, period=period)
Expand Down
16 changes: 16 additions & 0 deletions garth/stats/hydration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from typing import ClassVar

from pydantic.dataclasses import dataclass

from ._base import Stats

BASE_PATH = "/usersummary-service/stats/hydration"


@dataclass(frozen=True)
class DailyHydration(Stats):
value_in_ml: float
goal_in_ml: float

_path: ClassVar[str] = f"{BASE_PATH}/daily/{{start}}/{{end}}"
_page_size: ClassVar[int] = 28
2 changes: 1 addition & 1 deletion garth/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.46"
__version__ = "0.4.47"
4 changes: 2 additions & 2 deletions tests/data/test_hrv_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_hrv_data_get(authed_client: Client):
hrv_data = HRVData.get("2023-07-20", client=authed_client)
assert hrv_data
Expand All @@ -16,7 +16,7 @@ def test_hrv_data_get(authed_client: Client):
assert HRVData.get("2021-07-20", client=authed_client) is None


@pytest.mark.vcr
@pytest.mark.vcr()
def test_hrv_data_list(authed_client: Client):
days = 2
end = date(2023, 7, 20)
Expand Down
4 changes: 2 additions & 2 deletions tests/data/test_sleep_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_sleep_data_get(authed_client: Client):
sleep_data = SleepData.get("2021-07-20", client=authed_client)
assert sleep_data
Expand All @@ -15,7 +15,7 @@ def test_sleep_data_get(authed_client: Client):
assert sleep_data.daily_sleep_dto.sleep_end


@pytest.mark.vcr
@pytest.mark.vcr()
def test_sleep_data_list(authed_client: Client):
end = date(2021, 7, 20)
days = 20
Expand Down
73 changes: 73 additions & 0 deletions tests/stats/cassettes/test_daily_hydration.yaml
matin marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
interactions:
- request:
body: null
headers:
Accept:
- '*/*'
Accept-Encoding:
- gzip, deflate
Authorization:
- Bearer SANITIZED
Connection:
- keep-alive
User-Agent:
- Mozilla/5.0 (iPhone; CPU iPhone OS 16_5 like Mac OS X) AppleWebKit/605.1.15
(KHTML, like Gecko) Mobile/15E148
method: GET
matin marked this conversation as resolved.
Show resolved Hide resolved
uri: https://connectapi.garmin.com/usersummary-service/stats/hydration/daily/2024-06-29/2024-06-29
response:
body:
string: '[{"calendarDate": "2024-06-29", "valueInML": 1750.0, "goalInML": 2800.0}]'
headers:
CF-Cache-Status:
- DYNAMIC
CF-RAY:
- 89baf564a97ac302-IAH
Cache-Control:
- no-cache, no-store, private
Connection:
- keep-alive
Content-Type:
- application/json
Date:
- Sun, 30 Jun 2024 03:09:37 GMT
NEL:
- '{"success_fraction":0.01,"report_to":"cf-nel","max_age":604800}'
Report-To:
- '{"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v4?s=GRxjJQH4AeepFPPzqKaKzuF%2FwWsdMivGK4yskWFLewsm9h57VSu9AdrdMi9bdST3vzQ4oVJDxEfU4vR6EXLlsbom6nYoo6nCAtih9MJ3sIlW7aih0DTEBXrNcwILDtsSsmHmUe%2FM1A%3D%3D"}],"group":"cf-nel","max_age":604800}'
Server:
- cloudflare
Set-Cookie:
- ADRUM_BTa=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BTa=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
Transfer-Encoding:
- chunked
alt-svc:
- h3=":443"; ma=86400
pragma:
- no-cache
set-cookie:
- ADRUM_BTa=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BTa=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- ADRUM_BT1=SANITIZED; Max-Age=SANITIZED; Expires=SANITIZED; Path=SANITIZED;
Secure
- _cfuvid=SANITIZED; path=SANITIZED; domain=SANITIZED; HttpOnly; Secure; SameSite=SANITIZED
status:
code: 200
matin marked this conversation as resolved.
Show resolved Hide resolved
message: OK
version: 1
8 changes: 4 additions & 4 deletions tests/stats/test_hrv.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_hrv(authed_client: Client):
end = date(2023, 7, 20)
days = 20
Expand All @@ -15,7 +15,7 @@ def test_daily_hrv(authed_client: Client):
assert len(daily_hrv) == days


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_hrv_paginate(authed_client: Client):
end = date(2023, 7, 20)
days = 40
Expand All @@ -24,14 +24,14 @@ def test_daily_hrv_paginate(authed_client: Client):
assert len(daily_hrv) == days


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_hrv_no_results(authed_client: Client):
end = date(1990, 7, 20)
daily_hrv = DailyHRV.list(end, client=authed_client)
assert daily_hrv == []
matin marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_hrv_paginate_no_results(authed_client: Client):
end = date(1990, 7, 20)
days = 40
Expand Down
13 changes: 13 additions & 0 deletions tests/stats/test_hydration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from datetime import date

import pytest

from garth import DailyHydration
from garth.http import Client


@pytest.mark.vcr()
def test_daily_hydration(authed_client: Client):
matin marked this conversation as resolved.
Show resolved Hide resolved
end = date(2024, 6, 29)
daily_hydration = DailyHydration.list(end, client=authed_client)
assert daily_hydration[-1].calendar_date == end
matin marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 2 additions & 2 deletions tests/stats/test_intensity_minutes.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_intensity_minutes(authed_client: Client):
end = date(2023, 7, 20)
days = 20
Expand All @@ -15,7 +15,7 @@ def test_daily_intensity_minutes(authed_client: Client):
assert len(daily_im) == days


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_weekly_intensity_minutes(authed_client: Client):
end = date(2023, 7, 20)
weeks = 12
Expand Down
2 changes: 1 addition & 1 deletion tests/stats/test_sleep_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
def test_daily_sleep(authed_client: Client):
end = date(2023, 7, 20)
days = 20
Expand Down
4 changes: 2 additions & 2 deletions tests/stats/test_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_steps(authed_client: Client):
end = date(2023, 7, 20)
days = 20
Expand All @@ -15,7 +15,7 @@ def test_daily_steps(authed_client: Client):
assert len(daily_steps) == days


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_weekly_steps(authed_client: Client):
end = date(2023, 7, 20)
weeks = 52
Expand Down
10 changes: 5 additions & 5 deletions tests/stats/test_stress.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from garth.http import Client


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_stress(authed_client: Client):
end = date(2023, 7, 20)
days = 20
Expand All @@ -15,15 +15,15 @@ def test_daily_stress(authed_client: Client):
assert len(daily_stress) == days


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_daily_stress_pagination(authed_client: Client):
end = date(2023, 7, 20)
days = 60
daily_stress = DailyStress.list(end, days, client=authed_client)
assert len(daily_stress) == days
matin marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_weekly_stress(authed_client: Client):
end = date(2023, 7, 20)
weeks = 52
Expand All @@ -32,7 +32,7 @@ def test_weekly_stress(authed_client: Client):
assert weekly_stress[-1].calendar_date == end - timedelta(days=6)


@pytest.mark.vcr
@pytest.mark.vcr()
matin marked this conversation as resolved.
Show resolved Hide resolved
def test_weekly_stress_pagination(authed_client: Client):
end = date(2023, 7, 20)
weeks = 60
Expand All @@ -41,7 +41,7 @@ def test_weekly_stress_pagination(authed_client: Client):
assert weekly_stress[-1].calendar_date == end - timedelta(days=6)


@pytest.mark.vcr
@pytest.mark.vcr()
def test_weekly_stress_beyond_data(authed_client: Client):
end = date(2023, 7, 20)
weeks = 1000
Expand Down
Loading
Loading