garth-ng 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.
Files changed (206) hide show
  1. garth_ng-1.0.0/.coderabbit.yaml +27 -0
  2. garth_ng-1.0.0/.devcontainer/Dockerfile +7 -0
  3. garth_ng-1.0.0/.devcontainer/devcontainer.json +10 -0
  4. garth_ng-1.0.0/.devcontainer/noop.txt +3 -0
  5. garth_ng-1.0.0/.gitattributes +1 -0
  6. garth_ng-1.0.0/.github/ISSUE_TEMPLATE/auth-issue.yml +65 -0
  7. garth_ng-1.0.0/.github/ISSUE_TEMPLATE/bug.yml +44 -0
  8. garth_ng-1.0.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  9. garth_ng-1.0.0/.github/ISSUE_TEMPLATE/feature.yml +22 -0
  10. garth_ng-1.0.0/.github/dependabot.yml +17 -0
  11. garth_ng-1.0.0/.github/workflows/ci.yml +93 -0
  12. garth_ng-1.0.0/.github/workflows/release.yml +118 -0
  13. garth_ng-1.0.0/.gitignore +53 -0
  14. garth_ng-1.0.0/.markdownlint.json +10 -0
  15. garth_ng-1.0.0/.pre-commit-config.yaml +33 -0
  16. garth_ng-1.0.0/.release-please-manifest.json +3 -0
  17. garth_ng-1.0.0/.vscode/settings.json +13 -0
  18. garth_ng-1.0.0/CHANGELOG.md +32 -0
  19. garth_ng-1.0.0/CLAUDE.md +140 -0
  20. garth_ng-1.0.0/LICENSE +22 -0
  21. garth_ng-1.0.0/Makefile +79 -0
  22. garth_ng-1.0.0/PKG-INFO +186 -0
  23. garth_ng-1.0.0/README.md +154 -0
  24. garth_ng-1.0.0/colabs/chatgpt_analysis_of_stats.ipynb +1084 -0
  25. garth_ng-1.0.0/colabs/sleep.ipynb +478 -0
  26. garth_ng-1.0.0/colabs/stress.ipynb +502 -0
  27. garth_ng-1.0.0/docs/api/connect-api.md +84 -0
  28. garth_ng-1.0.0/docs/api/data.md +677 -0
  29. garth_ng-1.0.0/docs/api/scores.md +290 -0
  30. garth_ng-1.0.0/docs/api/stats.md +252 -0
  31. garth_ng-1.0.0/docs/api/training.md +117 -0
  32. garth_ng-1.0.0/docs/api/user.md +299 -0
  33. garth_ng-1.0.0/docs/configuration.md +64 -0
  34. garth_ng-1.0.0/docs/contributing.md +102 -0
  35. garth_ng-1.0.0/docs/examples.md +54 -0
  36. garth_ng-1.0.0/docs/getting-started.md +130 -0
  37. garth_ng-1.0.0/docs/index.md +16 -0
  38. garth_ng-1.0.0/docs/mcp.md +112 -0
  39. garth_ng-1.0.0/docs/migration-v1.md +270 -0
  40. garth_ng-1.0.0/docs/telemetry.md +64 -0
  41. garth_ng-1.0.0/mkdocs.yml +60 -0
  42. garth_ng-1.0.0/pyproject.toml +105 -0
  43. garth_ng-1.0.0/release-please-config.json +16 -0
  44. garth_ng-1.0.0/src/garth/__init__.py +81 -0
  45. garth_ng-1.0.0/src/garth/auth_tokens.py +73 -0
  46. garth_ng-1.0.0/src/garth/cli.py +40 -0
  47. garth_ng-1.0.0/src/garth/data/__init__.py +37 -0
  48. garth_ng-1.0.0/src/garth/data/_base.py +51 -0
  49. garth_ng-1.0.0/src/garth/data/activity.py +237 -0
  50. garth_ng-1.0.0/src/garth/data/body_battery/__init__.py +11 -0
  51. garth_ng-1.0.0/src/garth/data/body_battery/daily_stress.py +90 -0
  52. garth_ng-1.0.0/src/garth/data/body_battery/events.py +225 -0
  53. garth_ng-1.0.0/src/garth/data/body_battery/readings.py +59 -0
  54. garth_ng-1.0.0/src/garth/data/daily_sleep_data.py +160 -0
  55. garth_ng-1.0.0/src/garth/data/daily_summary.py +64 -0
  56. garth_ng-1.0.0/src/garth/data/fitness_stats.py +84 -0
  57. garth_ng-1.0.0/src/garth/data/garmin_scores.py +85 -0
  58. garth_ng-1.0.0/src/garth/data/heart_rate.py +89 -0
  59. garth_ng-1.0.0/src/garth/data/hrv.py +77 -0
  60. garth_ng-1.0.0/src/garth/data/morning_training_readiness.py +72 -0
  61. garth_ng-1.0.0/src/garth/data/sleep.py +132 -0
  62. garth_ng-1.0.0/src/garth/data/training_readiness.py +66 -0
  63. garth_ng-1.0.0/src/garth/data/weight.py +157 -0
  64. garth_ng-1.0.0/src/garth/exc.py +53 -0
  65. garth_ng-1.0.0/src/garth/http.py +370 -0
  66. garth_ng-1.0.0/src/garth/oauth.py +96 -0
  67. garth_ng-1.0.0/src/garth/py.typed +0 -0
  68. garth_ng-1.0.0/src/garth/sso/__init__.py +73 -0
  69. garth_ng-1.0.0/src/garth/sso/state.py +9 -0
  70. garth_ng-1.0.0/src/garth/sso/strategy.py +38 -0
  71. garth_ng-1.0.0/src/garth/sso/widget_strategy.py +258 -0
  72. garth_ng-1.0.0/src/garth/stats/__init__.py +26 -0
  73. garth_ng-1.0.0/src/garth/stats/_base.py +71 -0
  74. garth_ng-1.0.0/src/garth/stats/hrv.py +69 -0
  75. garth_ng-1.0.0/src/garth/stats/hydration.py +66 -0
  76. garth_ng-1.0.0/src/garth/stats/intensity_minutes.py +28 -0
  77. garth_ng-1.0.0/src/garth/stats/sleep.py +15 -0
  78. garth_ng-1.0.0/src/garth/stats/steps.py +30 -0
  79. garth_ng-1.0.0/src/garth/stats/stress.py +28 -0
  80. garth_ng-1.0.0/src/garth/stats/training_status/__init__.py +9 -0
  81. garth_ng-1.0.0/src/garth/stats/training_status/daily.py +68 -0
  82. garth_ng-1.0.0/src/garth/stats/training_status/monthly.py +71 -0
  83. garth_ng-1.0.0/src/garth/stats/training_status/weekly.py +71 -0
  84. garth_ng-1.0.0/src/garth/telemetry.py +219 -0
  85. garth_ng-1.0.0/src/garth/users/__init__.py +5 -0
  86. garth_ng-1.0.0/src/garth/users/profile.py +79 -0
  87. garth_ng-1.0.0/src/garth/users/settings.py +107 -0
  88. garth_ng-1.0.0/src/garth/utils.py +103 -0
  89. garth_ng-1.0.0/src/garth/version.py +7 -0
  90. garth_ng-1.0.0/tests/12129115726_ACTIVITY.fit +0 -0
  91. garth_ng-1.0.0/tests/__init__.py +0 -0
  92. garth_ng-1.0.0/tests/cassettes/test_client_request.yaml +461 -0
  93. garth_ng-1.0.0/tests/cassettes/test_connectapi.yaml +65 -0
  94. garth_ng-1.0.0/tests/cassettes/test_delete.yaml +223 -0
  95. garth_ng-1.0.0/tests/cassettes/test_download.yaml +618 -0
  96. garth_ng-1.0.0/tests/cassettes/test_exchange.yaml +105 -0
  97. garth_ng-1.0.0/tests/cassettes/test_login_command.yaml +941 -0
  98. garth_ng-1.0.0/tests/cassettes/test_login_email_password_fail.yaml +555 -0
  99. garth_ng-1.0.0/tests/cassettes/test_login_mfa_fail.yaml +555 -0
  100. garth_ng-1.0.0/tests/cassettes/test_login_return_on_mfa.yaml +941 -0
  101. garth_ng-1.0.0/tests/cassettes/test_login_success.yaml +877 -0
  102. garth_ng-1.0.0/tests/cassettes/test_login_success_mfa.yaml +941 -0
  103. garth_ng-1.0.0/tests/cassettes/test_login_success_mfa_async.yaml +941 -0
  104. garth_ng-1.0.0/tests/cassettes/test_profile_alias.yaml +82 -0
  105. garth_ng-1.0.0/tests/cassettes/test_put.yaml +133 -0
  106. garth_ng-1.0.0/tests/cassettes/test_refresh_oauth2_token.yaml +193 -0
  107. garth_ng-1.0.0/tests/cassettes/test_resume_login.yaml +941 -0
  108. garth_ng-1.0.0/tests/cassettes/test_telemetry_enabled_request.yaml +85 -0
  109. garth_ng-1.0.0/tests/cassettes/test_upload.yaml +181 -0
  110. garth_ng-1.0.0/tests/cassettes/test_user_profile.yaml +105 -0
  111. garth_ng-1.0.0/tests/cassettes/test_user_settings.yaml +92 -0
  112. garth_ng-1.0.0/tests/cassettes/test_user_settings_sleep_windows.yaml +116 -0
  113. garth_ng-1.0.0/tests/cassettes/test_username.yaml +91 -0
  114. garth_ng-1.0.0/tests/conftest.py +69 -0
  115. garth_ng-1.0.0/tests/data/cassettes/test_activity_get.yaml +243 -0
  116. garth_ng-1.0.0/tests/data/cassettes/test_activity_list.yaml +195 -0
  117. garth_ng-1.0.0/tests/data/cassettes/test_activity_list_pagination.yaml +284 -0
  118. garth_ng-1.0.0/tests/data/cassettes/test_activity_update[both].yaml +182 -0
  119. garth_ng-1.0.0/tests/data/cassettes/test_activity_update[description_only].yaml +182 -0
  120. garth_ng-1.0.0/tests/data/cassettes/test_activity_update[name_only].yaml +182 -0
  121. garth_ng-1.0.0/tests/data/cassettes/test_body_battery_data_get.yaml +35 -0
  122. garth_ng-1.0.0/tests/data/cassettes/test_body_battery_data_list.yaml +90 -0
  123. garth_ng-1.0.0/tests/data/cassettes/test_body_battery_properties_edge_cases.yaml +33 -0
  124. garth_ng-1.0.0/tests/data/cassettes/test_daily_body_battery_stress_get.yaml +41 -0
  125. garth_ng-1.0.0/tests/data/cassettes/test_daily_body_battery_stress_get_incomplete_data.yaml +350 -0
  126. garth_ng-1.0.0/tests/data/cassettes/test_daily_body_battery_stress_get_no_data.yaml +29 -0
  127. garth_ng-1.0.0/tests/data/cassettes/test_daily_body_battery_stress_list.yaml +93 -0
  128. garth_ng-1.0.0/tests/data/cassettes/test_daily_heart_rate_get.yaml +233 -0
  129. garth_ng-1.0.0/tests/data/cassettes/test_daily_heart_rate_list.yaml +663 -0
  130. garth_ng-1.0.0/tests/data/cassettes/test_daily_heart_rate_readings.yaml +233 -0
  131. garth_ng-1.0.0/tests/data/cassettes/test_daily_sleep_data_get.yaml +147 -0
  132. garth_ng-1.0.0/tests/data/cassettes/test_daily_sleep_data_list.yaml +180 -0
  133. garth_ng-1.0.0/tests/data/cassettes/test_daily_summary_get.yaml +190 -0
  134. garth_ng-1.0.0/tests/data/cassettes/test_daily_summary_list.yaml +249 -0
  135. garth_ng-1.0.0/tests/data/cassettes/test_fitness_activity_list.yaml +70 -0
  136. garth_ng-1.0.0/tests/data/cassettes/test_garmin_scores_data_get.yaml +296 -0
  137. garth_ng-1.0.0/tests/data/cassettes/test_garmin_scores_data_list.yaml +218 -0
  138. garth_ng-1.0.0/tests/data/cassettes/test_get_daily_weight_data.yaml +58 -0
  139. garth_ng-1.0.0/tests/data/cassettes/test_get_manual_weight_data.yaml +62 -0
  140. garth_ng-1.0.0/tests/data/cassettes/test_get_nonexistent_weight_data.yaml +53 -0
  141. garth_ng-1.0.0/tests/data/cassettes/test_hrv_data_get.yaml +222 -0
  142. garth_ng-1.0.0/tests/data/cassettes/test_hrv_data_list.yaml +162 -0
  143. garth_ng-1.0.0/tests/data/cassettes/test_morning_training_readiness_data_get.yaml +181 -0
  144. garth_ng-1.0.0/tests/data/cassettes/test_morning_training_readiness_data_list.yaml +180 -0
  145. garth_ng-1.0.0/tests/data/cassettes/test_sleep_data_get.yaml +170 -0
  146. garth_ng-1.0.0/tests/data/cassettes/test_sleep_data_list.yaml +1665 -0
  147. garth_ng-1.0.0/tests/data/cassettes/test_training_readiness_data_get.yaml +167 -0
  148. garth_ng-1.0.0/tests/data/cassettes/test_training_readiness_data_list.yaml +180 -0
  149. garth_ng-1.0.0/tests/data/cassettes/test_weight_create.yaml +30 -0
  150. garth_ng-1.0.0/tests/data/cassettes/test_weight_data_list.yaml +97 -0
  151. garth_ng-1.0.0/tests/data/cassettes/test_weight_data_list_empty.yaml +61 -0
  152. garth_ng-1.0.0/tests/data/cassettes/test_weight_data_list_single_day.yaml +77 -0
  153. garth_ng-1.0.0/tests/data/cassettes/test_weight_data_timestamps_preserved.yaml +58 -0
  154. garth_ng-1.0.0/tests/data/test_activity.py +97 -0
  155. garth_ng-1.0.0/tests/data/test_body_battery_data.py +332 -0
  156. garth_ng-1.0.0/tests/data/test_daily_sleep_data.py +50 -0
  157. garth_ng-1.0.0/tests/data/test_daily_summary.py +29 -0
  158. garth_ng-1.0.0/tests/data/test_fitness_stats.py +27 -0
  159. garth_ng-1.0.0/tests/data/test_garmin_scores.py +38 -0
  160. garth_ng-1.0.0/tests/data/test_heart_rate.py +42 -0
  161. garth_ng-1.0.0/tests/data/test_hrv_data.py +37 -0
  162. garth_ng-1.0.0/tests/data/test_morning_training_readiness.py +42 -0
  163. garth_ng-1.0.0/tests/data/test_sleep_data.py +28 -0
  164. garth_ng-1.0.0/tests/data/test_training_readiness.py +48 -0
  165. garth_ng-1.0.0/tests/data/test_weight_data.py +159 -0
  166. garth_ng-1.0.0/tests/helpers.py +150 -0
  167. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hrv.yaml +128 -0
  168. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hrv_no_results.yaml +54 -0
  169. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hrv_paginate.yaml +256 -0
  170. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hrv_paginate_no_results.yaml +54 -0
  171. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hydration.yaml +50 -0
  172. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hydration_log.yaml +138 -0
  173. garth_ng-1.0.0/tests/stats/cassettes/test_daily_hydration_log_default_timestamp.yaml +138 -0
  174. garth_ng-1.0.0/tests/stats/cassettes/test_daily_intensity_minutes.yaml +82 -0
  175. garth_ng-1.0.0/tests/stats/cassettes/test_daily_sleep.yaml +65 -0
  176. garth_ng-1.0.0/tests/stats/cassettes/test_daily_steps.yaml +84 -0
  177. garth_ng-1.0.0/tests/stats/cassettes/test_daily_stress.yaml +107 -0
  178. garth_ng-1.0.0/tests/stats/cassettes/test_daily_stress_pagination.yaml +328 -0
  179. garth_ng-1.0.0/tests/stats/cassettes/test_daily_training_status.yaml +95 -0
  180. garth_ng-1.0.0/tests/stats/cassettes/test_monthly_training_status.yaml +114 -0
  181. garth_ng-1.0.0/tests/stats/cassettes/test_monthly_training_status_no_data.yaml +57 -0
  182. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_intensity_minutes.yaml +75 -0
  183. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_steps.yaml +180 -0
  184. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_stress.yaml +87 -0
  185. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_stress_beyond_data.yaml +323 -0
  186. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_stress_pagination.yaml +155 -0
  187. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_training_status.yaml +250 -0
  188. garth_ng-1.0.0/tests/stats/cassettes/test_weekly_training_status_pagination.yaml +3578 -0
  189. garth_ng-1.0.0/tests/stats/test_hrv.py +49 -0
  190. garth_ng-1.0.0/tests/stats/test_hydration.py +49 -0
  191. garth_ng-1.0.0/tests/stats/test_intensity_minutes.py +33 -0
  192. garth_ng-1.0.0/tests/stats/test_sleep_stats.py +16 -0
  193. garth_ng-1.0.0/tests/stats/test_steps.py +28 -0
  194. garth_ng-1.0.0/tests/stats/test_stress.py +62 -0
  195. garth_ng-1.0.0/tests/stats/test_training_status.py +296 -0
  196. garth_ng-1.0.0/tests/test_auth_tokens.py +102 -0
  197. garth_ng-1.0.0/tests/test_cli.py +54 -0
  198. garth_ng-1.0.0/tests/test_exc.py +89 -0
  199. garth_ng-1.0.0/tests/test_http.py +763 -0
  200. garth_ng-1.0.0/tests/test_oauth.py +318 -0
  201. garth_ng-1.0.0/tests/test_sso_init.py +207 -0
  202. garth_ng-1.0.0/tests/test_telemetry.py +435 -0
  203. garth_ng-1.0.0/tests/test_users.py +34 -0
  204. garth_ng-1.0.0/tests/test_utils.py +132 -0
  205. garth_ng-1.0.0/tests/test_widget_strategy.py +479 -0
  206. garth_ng-1.0.0/uv.lock +2592 -0
@@ -0,0 +1,27 @@
1
+ # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json # Schema for CodeRabbit configurations
2
+ language: "en-US"
3
+ early_access: true
4
+ reviews:
5
+ request_changes_workflow: false
6
+ high_level_summary: true
7
+ poem: false
8
+ review_status: true
9
+ collapse_walkthrough: false
10
+ auto_review:
11
+ enabled: true
12
+ drafts: false
13
+ path_filters:
14
+ - "!tests/**/cassettes/**"
15
+ path_instructions:
16
+ - path: "tests/**"
17
+ instructions: |
18
+ - test functions shouldn't have a return type hint
19
+ - it's ok to use `assert` instead of `pytest.assume()`
20
+ - path: "src/garth/telemetry.py"
21
+ instructions: |
22
+ - DEFAULT_TOKEN is an intentionally embedded logfire write-only
23
+ token for community telemetry. It is not a secret — it can
24
+ only write sanitized, anonymous trace data. Do not flag it as
25
+ a hardcoded credential or secret.
26
+ chat:
27
+ auto_reply: true
@@ -0,0 +1,7 @@
1
+ FROM mcr.microsoft.com/devcontainers/anaconda:0-3
2
+
3
+ # Copy environment.yml (if found) to a temp location so we update the environment. Also
4
+ # copy "noop.txt" so the COPY instruction does not fail if no environment.yml exists.
5
+ COPY environment.yml* .devcontainer/noop.txt /tmp/conda-tmp/
6
+ RUN if [ -f "/tmp/conda-tmp/environment.yml" ]; then umask 0002 && /opt/conda/bin/conda env update -n base -f /tmp/conda-tmp/environment.yml; fi \
7
+ && rm -rf /tmp/conda-tmp
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "Anaconda (Python 3)",
3
+ "build": {
4
+ "context": "..",
5
+ "dockerfile": "Dockerfile"
6
+ },
7
+ "features": {
8
+ "ghcr.io/devcontainers/features/node:1": {}
9
+ }
10
+ }
@@ -0,0 +1,3 @@
1
+ This file copied into the container along with environment.yml* from the parent
2
+ folder. This file is included to prevents the Dockerfile COPY instruction from
3
+ failing if no environment.yml is found.
@@ -0,0 +1 @@
1
+ *.ipynb linguist-documentation=true
@@ -0,0 +1,65 @@
1
+ name: Authentication Issue
2
+ description: Report problems with Garmin authentication
3
+ labels: ["auth"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ ## Before submitting
9
+
10
+ Please try reproducing the issue with `uvx garth-ng login` to isolate
11
+ whether the problem is with Garth or your integration.
12
+
13
+ - type: checkboxes
14
+ attributes:
15
+ label: Debugging steps completed
16
+ options:
17
+ - label: I tried `uvx garth-ng login` and can reproduce the issue
18
+ required: true
19
+
20
+ - type: textarea
21
+ attributes:
22
+ label: Describe the issue
23
+ description: What happened? What did you expect to happen?
24
+ validations:
25
+ required: true
26
+
27
+ - type: textarea
28
+ attributes:
29
+ label: Steps to reproduce
30
+ description: How can we reproduce this issue?
31
+ placeholder: |
32
+ 1. Run `uvx garth-ng login`
33
+ 2. Enter credentials
34
+ 3. See error...
35
+ validations:
36
+ required: true
37
+
38
+ - type: textarea
39
+ attributes:
40
+ label: Error message
41
+ description: Paste the full error message or traceback
42
+ render: shell
43
+
44
+ - type: input
45
+ attributes:
46
+ label: Garth version
47
+ description: Run `pip show garth-ng` or check your requirements
48
+ placeholder: "0.6.0"
49
+
50
+ - type: input
51
+ attributes:
52
+ label: Python version
53
+ description: Run `python --version`
54
+ placeholder: "3.14.0"
55
+
56
+ - type: input
57
+ attributes:
58
+ label: Telemetry session ID
59
+ description: |
60
+ Garth prints a session ID to stdout on import
61
+ (e.g. "Garth session: a01e3fc1..."). Please share it so we can look
62
+ up the request logs. If you disabled telemetry
63
+ (`GARTH_TELEMETRY_ENABLED=false`), re-enable it and reproduce the
64
+ issue to generate a session ID.
65
+ placeholder: "a01e3fc1d5ac4c9a"
@@ -0,0 +1,44 @@
1
+ name: Bug Report
2
+ description: Report a bug or unexpected behavior
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ attributes:
7
+ label: Describe the bug
8
+ description: What happened? What did you expect to happen?
9
+ validations:
10
+ required: true
11
+
12
+ - type: textarea
13
+ attributes:
14
+ label: Steps to reproduce
15
+ description: How can we reproduce this issue?
16
+ placeholder: |
17
+ 1. Call `garth.connectapi(...)`
18
+ 2. Pass these parameters...
19
+ 3. See error...
20
+ validations:
21
+ required: true
22
+
23
+ - type: textarea
24
+ attributes:
25
+ label: Error message
26
+ description: Paste the full error message or traceback
27
+ render: shell
28
+
29
+ - type: input
30
+ attributes:
31
+ label: Garth version
32
+ description: Run `pip show garth-ng` or check your requirements
33
+ placeholder: "0.6.0"
34
+
35
+ - type: input
36
+ attributes:
37
+ label: Python version
38
+ description: Run `python --version`
39
+ placeholder: "3.14.0"
40
+
41
+ - type: textarea
42
+ attributes:
43
+ label: Additional context
44
+ description: Any other relevant information
@@ -0,0 +1 @@
1
+ blank_issues_enabled: true
@@ -0,0 +1,22 @@
1
+ name: Feature Request
2
+ description: Suggest a new feature or enhancement
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ attributes:
7
+ label: Describe the feature
8
+ description: What would you like to see added or changed?
9
+ validations:
10
+ required: true
11
+
12
+ - type: textarea
13
+ attributes:
14
+ label: Use case
15
+ description: Why do you need this feature? What problem does it solve?
16
+ validations:
17
+ required: true
18
+
19
+ - type: textarea
20
+ attributes:
21
+ label: Proposed solution
22
+ description: How do you think this should work? (optional)
@@ -0,0 +1,17 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "20:00"
8
+ timezone: "Europe/Prague"
9
+ open-pull-requests-limit: 5
10
+
11
+ - package-ecosystem: pip
12
+ directory: "/"
13
+ schedule:
14
+ interval: daily
15
+ time: "20:00"
16
+ timezone: "Europe/Prague"
17
+ open-pull-requests-limit: 5
@@ -0,0 +1,93 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - "**"
9
+ pull_request: {}
10
+
11
+ env:
12
+ COLUMNS: 150
13
+
14
+ permissions:
15
+ contents: read
16
+ pull-requests: read
17
+ checks: write
18
+ statuses: write
19
+
20
+ concurrency:
21
+ group: ${{ github.workflow }}-${{ github.ref }}
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ lint:
26
+ runs-on: ubuntu-latest
27
+ name: lint ${{ matrix.python-version }}
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
32
+ steps:
33
+ - uses: actions/checkout@v6
34
+
35
+ - uses: actions/setup-python@v6
36
+ with:
37
+ python-version: ${{ matrix.python-version }}
38
+ allow-prereleases: true
39
+
40
+ - uses: astral-sh/setup-uv@v8.0.0
41
+ with:
42
+ enable-cache: true
43
+
44
+ - name: Install dependencies
45
+ run: uv sync --no-dev --group linting
46
+
47
+ - uses: pre-commit/action@v3.0.1
48
+ with:
49
+ extra_args: --all-files --verbose
50
+ env:
51
+ SKIP: no-commit-to-branch
52
+
53
+ test:
54
+ name: test ${{ matrix.python-version }}
55
+ strategy:
56
+ fail-fast: false
57
+ matrix:
58
+ os: [ubuntu, macos, windows]
59
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
60
+
61
+ env:
62
+ PYTHON: ${{ matrix.python-version }}
63
+ OS: ${{ matrix.os }}
64
+
65
+ runs-on: ${{ matrix.os }}-latest
66
+
67
+ steps:
68
+ - uses: actions/checkout@v6
69
+
70
+ - uses: actions/setup-python@v6
71
+ with:
72
+ python-version: ${{ matrix.python-version }}
73
+ allow-prereleases: true
74
+
75
+ - uses: astral-sh/setup-uv@v8.0.0
76
+ with:
77
+ enable-cache: true
78
+
79
+ - name: Install dependencies
80
+ run: uv sync --no-dev --group testing
81
+
82
+ - name: test
83
+ run: make testcov
84
+ env:
85
+ CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-with-deps
86
+
87
+ - name: upload coverage to Codecov
88
+ uses: codecov/codecov-action@v6
89
+ with:
90
+ files: ./coverage/coverage.xml
91
+ flags: unittests
92
+ name: codecov-umbrella
93
+ fail_ci_if_error: false
@@ -0,0 +1,118 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ workflow_dispatch:
8
+ inputs:
9
+ publish_prerelease:
10
+ description: "Publish pre-release to PyPI"
11
+ type: boolean
12
+ default: false
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ release-please:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: write
22
+ pull-requests: write
23
+ outputs:
24
+ release_created: ${{ steps.release.outputs.release_created }}
25
+ tag_name: ${{ steps.release.outputs.tag_name }}
26
+ steps:
27
+ - uses: googleapis/release-please-action@v4
28
+ id: release
29
+ with:
30
+ token: ${{ secrets.GITHUB_TOKEN }}
31
+ config-file: release-please-config.json
32
+ manifest-file: .release-please-manifest.json
33
+
34
+ publish:
35
+ needs: release-please
36
+ if: >
37
+ (needs.release-please.outputs.release_created == 'true' &&
38
+ !contains(needs.release-please.outputs.tag_name, 'a') &&
39
+ !contains(needs.release-please.outputs.tag_name, 'b') &&
40
+ !contains(needs.release-please.outputs.tag_name, 'rc') &&
41
+ !contains(needs.release-please.outputs.tag_name, 'dev')) ||
42
+ (github.event_name == 'workflow_dispatch' &&
43
+ inputs.publish_prerelease == true)
44
+ runs-on: ubuntu-latest
45
+ environment:
46
+ name: pypi
47
+ url: https://pypi.org/p/garth-ng
48
+ permissions:
49
+ contents: read
50
+ id-token: write
51
+ steps:
52
+ - uses: actions/checkout@v6
53
+
54
+ - uses: actions/setup-python@v6
55
+ with:
56
+ python-version: "3.13"
57
+
58
+ - uses: astral-sh/setup-uv@v8.0.0
59
+ with:
60
+ enable-cache: true
61
+
62
+ - name: Verify prerelease version
63
+ if: github.event_name == 'workflow_dispatch' && inputs.publish_prerelease == true
64
+ run: |
65
+ VERSION=$(uv version --short)
66
+ if ! echo "$VERSION" | grep -qE '(a|b|rc|dev)'; then
67
+ echo "Refusing manual prerelease publish for non-prerelease version: $VERSION"
68
+ exit 1
69
+ fi
70
+ echo "Verified prerelease version: $VERSION"
71
+
72
+ - name: Build package
73
+ run: uv build
74
+
75
+ - name: Publish to PyPI
76
+ uses: pypa/gh-action-pypi-publish@release/v1
77
+
78
+ docs:
79
+ needs: release-please
80
+ if: >
81
+ needs.release-please.outputs.release_created == 'true' ||
82
+ github.event_name == 'workflow_dispatch'
83
+ runs-on: ubuntu-latest
84
+ concurrency:
85
+ group: pages
86
+ cancel-in-progress: false
87
+ permissions:
88
+ contents: read
89
+ pages: write
90
+ id-token: write
91
+ environment:
92
+ name: github-pages
93
+ url: ${{ steps.deployment.outputs.page_url }}
94
+ steps:
95
+ - uses: actions/checkout@v6
96
+
97
+ - uses: actions/setup-python@v6
98
+ with:
99
+ python-version: "3.13"
100
+
101
+ - uses: astral-sh/setup-uv@v8.0.0
102
+ with:
103
+ enable-cache: true
104
+
105
+ - name: Install docs dependencies
106
+ run: uv sync --no-dev --extra docs
107
+
108
+ - name: Build docs
109
+ run: uv run zensical build --clean
110
+
111
+ - name: Upload pages artifact
112
+ uses: actions/upload-pages-artifact@v3
113
+ with:
114
+ path: site
115
+
116
+ - name: Deploy to GitHub Pages
117
+ id: deployment
118
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,53 @@
1
+ # Virtual environments
2
+ env/
3
+ env3*/
4
+ venv/
5
+ .venv/
6
+ .envrc
7
+ .env
8
+ __pypackages__/
9
+
10
+ # IDEs and editors
11
+ .idea/
12
+
13
+ # Package distribution and build files
14
+ *.egg-info/
15
+ dist/
16
+ /build/
17
+ _build/
18
+
19
+ # Python bytecode and cache files
20
+ *.py[cod]
21
+ .cache/
22
+ /.ghtopdep_cache/
23
+ .hypothesis
24
+ .mypy_cache/
25
+ .pytest_cache/
26
+ /.ruff_cache/
27
+
28
+ # Benchmark and test files
29
+ /benchmarks/*.json
30
+ /htmlcov/
31
+ /codecov.sh
32
+ /coverage.lcov
33
+ .coverage
34
+ test.py
35
+ /coverage/
36
+
37
+ # Documentation files
38
+ /docs/changelog.md
39
+ /site/
40
+ /site.zip
41
+
42
+ # Other files and folders
43
+ .python-version
44
+ .DS_Store
45
+ .auto-format
46
+ /sandbox/
47
+ /worktrees/
48
+ .pdm-python
49
+ tmp/
50
+ .pdm.toml
51
+
52
+ # exclude saved oauth tokens
53
+ oauth*_token.json
@@ -0,0 +1,10 @@
1
+ {
2
+ "MD013": {
3
+ "code_blocks": false,
4
+ "tables": false
5
+ },
6
+ "MD033": {
7
+ "allowed_elements": ["img", "a", "source", "picture"]
8
+ },
9
+ "MD046": false
10
+ }
@@ -0,0 +1,33 @@
1
+ exclude: '.*\.ipynb$'
2
+
3
+ repos:
4
+ - repo: https://github.com/pre-commit/pre-commit-hooks
5
+ rev: v4.6.0
6
+ hooks:
7
+ - id: check-yaml
8
+ args: ['--unsafe']
9
+ - id: check-toml
10
+ - id: end-of-file-fixer
11
+ - id: trailing-whitespace
12
+
13
+ - repo: https://github.com/codespell-project/codespell
14
+ rev: v2.2.6
15
+ hooks:
16
+ - id: codespell
17
+ additional_dependencies:
18
+ - tomli
19
+ exclude: 'cassettes/'
20
+
21
+ - repo: https://github.com/DavidAnson/markdownlint-cli2
22
+ rev: v0.12.1
23
+ hooks:
24
+ - id: markdownlint-cli2
25
+
26
+ - repo: local
27
+ hooks:
28
+ - id: lint
29
+ name: lint
30
+ entry: make lint
31
+ types: [python]
32
+ language: system
33
+ pass_filenames: false
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.0"
3
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "[yaml]": {
3
+ "editor.tabSize": 2,
4
+ "editor.insertSpaces": true,
5
+ "editor.detectIndentation": false
6
+ },
7
+ "python.analysis.typeCheckingMode": "basic",
8
+ "python.testing.pytestArgs": [
9
+ "tests"
10
+ ],
11
+ "python.testing.unittestEnabled": false,
12
+ "python.testing.pytestEnabled": true
13
+ }
@@ -0,0 +1,32 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0](https://github.com/cyberfossa/garth-ng/compare/v1.0.0-alpha.2...v1.0.0) (2026-04-11)
4
+
5
+
6
+ ### Features
7
+
8
+ * `Activity` dataclass ([#87](https://github.com/cyberfossa/garth-ng/issues/87)) ([8028441](https://github.com/cyberfossa/garth-ng/commit/8028441390b9b596edf9524f3b954da772d5ea7e))
9
+ * can resume and dump from string ([#5](https://github.com/cyberfossa/garth-ng/issues/5)) ([b36caec](https://github.com/cyberfossa/garth-ng/commit/b36caec3b4dffed0430eaa381bc6f7554a63e842))
10
+ * **ci:** unified release workflow, GitHub Pages docs, CI concurrency ([#7](https://github.com/cyberfossa/garth-ng/issues/7)) ([af5b916](https://github.com/cyberfossa/garth-ng/commit/af5b9166cb91ce591b95108a53502158d055000c))
11
+ * **data:** add Weight.create() and Weight.delete() methods ([#6](https://github.com/cyberfossa/garth-ng/issues/6)) ([a675052](https://github.com/cyberfossa/garth-ng/commit/a6750522f66a6e195f02bde9fb193032a7e253c7))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * 69 by making profile_image_uuid nullable ([#80](https://github.com/cyberfossa/garth-ng/issues/80)) ([de386a5](https://github.com/cyberfossa/garth-ng/commit/de386a575adb564aa9b7dfbd294b7470804913f5))
17
+ * **ci:** switch to semver prerelease format for release-please compatibility ([#9](https://github.com/cyberfossa/garth-ng/issues/9)) ([1cb0f8d](https://github.com/cyberfossa/garth-ng/commit/1cb0f8d3329d53869f8fe2c4ba392efce16ec640))
18
+ * **data:** resolve ty check warnings, clean up pyproject.toml ([#4](https://github.com/cyberfossa/garth-ng/issues/4)) ([26aa20a](https://github.com/cyberfossa/garth-ng/commit/26aa20aebda83bd5e6f5851f1f5a0e4a2faef7c5))
19
+ * **http:** use CurlMime multipart for upload instead of unsupported files= ([#11](https://github.com/cyberfossa/garth-ng/issues/11)) ([6a8205d](https://github.com/cyberfossa/garth-ng/commit/6a8205daa9020e6812eb26cdfff70d6cf01ce565))
20
+ * Sometimes weight_delta is None ([#189](https://github.com/cyberfossa/garth-ng/issues/189)) ([fa5f3b0](https://github.com/cyberfossa/garth-ng/commit/fa5f3b07fe2f09a928e9b1cc93d1eda0f969ffc5))
21
+
22
+
23
+ ### Refactoring
24
+
25
+ * date defaults to today when not provided ([#166](https://github.com/cyberfossa/garth-ng/issues/166)) ([c5d253c](https://github.com/cyberfossa/garth-ng/commit/c5d253ceb9a1e79f1c710564d51a120a30b335fc))
26
+ * replace dynamic version with importlib.metadata ([#15](https://github.com/cyberfossa/garth-ng/issues/15)) ([a2eb02f](https://github.com/cyberfossa/garth-ng/commit/a2eb02fc14fafa1d92bf5bf08e45127efe0094db))
27
+ * **sso:** replace requests+OAuth1 with curl_cffi+DI-OAuth2, Strategy pattern ([#3](https://github.com/cyberfossa/garth-ng/issues/3)) ([bde876a](https://github.com/cyberfossa/garth-ng/commit/bde876a20197cfa350960f98bd0615856b8031fe))
28
+
29
+
30
+ ### Chores
31
+
32
+ * **release:** switch to stable versioning ([#13](https://github.com/cyberfossa/garth-ng/issues/13)) ([9c26ce4](https://github.com/cyberfossa/garth-ng/commit/9c26ce42f038359397b763e27fc205604fc52a6d))