sweatstack 0.12.0__tar.gz → 0.13.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack
3
- Version: 0.12.0
3
+ Version: 0.13.0
4
4
  Summary: The official Python client for SweatStack
5
5
  Author-email: Aart Goossens <aart@gssns.io>
6
6
  Requires-Python: >=3.12
@@ -12,6 +12,9 @@ Provides-Extra: jupyterlab
12
12
  Requires-Dist: ipython>=8.31.0; extra == 'jupyterlab'
13
13
  Requires-Dist: jupyterlab>=4.3.4; extra == 'jupyterlab'
14
14
  Requires-Dist: matplotlib; extra == 'jupyterlab'
15
+ Provides-Extra: streamlit
16
+ Requires-Dist: streamlit-cookies-controller>=0.0.4; extra == 'streamlit'
17
+ Requires-Dist: streamlit>=1.42.0; extra == 'streamlit'
15
18
  Description-Content-Type: text/markdown
16
19
 
17
20
  # SweatStack Python client library
@@ -82,4 +85,33 @@ activities = client.list_activities()
82
85
 
83
86
  Although both interfaces are feature-equivalent, they serve different purposes:
84
87
  - The singleton interface is the default and recommended interface. It is intended for most use cases and is the easiest to use.
85
- - The class-based interface is intended for more advanced use cases, such as when you need to authenticate multiple users at the same time or in multi-threaded applications.
88
+ - The class-based interface is intended for more advanced use cases, such as when you need to authenticate multiple users at the same time or in multi-threaded applications.
89
+
90
+
91
+ ## Streamlit integration
92
+
93
+ The `sweatstack.streamlit` module provides a Streamlit integration for SweatStack. This requires the optional `streamlit` dependency that can be installed with:
94
+ ```
95
+ uv pip install 'sweatstack[streamlit]'
96
+ ```
97
+
98
+ The `StreamlitAuth` class is a Streamlit component that handles the OAuth2 authentication flow. It provides a `st.authenticate()` function that can be used to authenticate the user.
99
+
100
+ Example usage:
101
+
102
+ ```python
103
+ from sweatstack.streamlit import StreamlitAuth
104
+
105
+ auth = StreamlitAuth()
106
+
107
+ with st.sidebar:
108
+ st.authenticate()
109
+
110
+ if not auth.is_authenticated():
111
+ st.write("User is not authenticated")
112
+ st.stop()
113
+
114
+ st.write("User is authenticated")
115
+
116
+ auth.client.get_latest_activity()
117
+ ```
@@ -66,4 +66,33 @@ activities = client.list_activities()
66
66
 
67
67
  Although both interfaces are feature-equivalent, they serve different purposes:
68
68
  - The singleton interface is the default and recommended interface. It is intended for most use cases and is the easiest to use.
69
- - The class-based interface is intended for more advanced use cases, such as when you need to authenticate multiple users at the same time or in multi-threaded applications.
69
+ - The class-based interface is intended for more advanced use cases, such as when you need to authenticate multiple users at the same time or in multi-threaded applications.
70
+
71
+
72
+ ## Streamlit integration
73
+
74
+ The `sweatstack.streamlit` module provides a Streamlit integration for SweatStack. This requires the optional `streamlit` dependency that can be installed with:
75
+ ```
76
+ uv pip install 'sweatstack[streamlit]'
77
+ ```
78
+
79
+ The `StreamlitAuth` class is a Streamlit component that handles the OAuth2 authentication flow. It provides a `st.authenticate()` function that can be used to authenticate the user.
80
+
81
+ Example usage:
82
+
83
+ ```python
84
+ from sweatstack.streamlit import StreamlitAuth
85
+
86
+ auth = StreamlitAuth()
87
+
88
+ with st.sidebar:
89
+ st.authenticate()
90
+
91
+ if not auth.is_authenticated():
92
+ st.write("User is not authenticated")
93
+ st.stop()
94
+
95
+ st.write("User is authenticated")
96
+
97
+ auth.client.get_latest_activity()
98
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sweatstack"
3
- version = "0.12.0"
3
+ version = "0.13.0"
4
4
  description = "The official Python client for SweatStack"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -20,6 +20,10 @@ jupyterlab = [
20
20
  "jupyterlab>=4.3.4",
21
21
  "matplotlib",
22
22
  ]
23
+ streamlit = [
24
+ "streamlit>=1.42.0",
25
+ "streamlit-cookies-controller>=0.0.4",
26
+ ]
23
27
 
24
28
  [build-system]
25
29
  requires = ["hatchling"]
@@ -20,9 +20,9 @@ import pandas as pd
20
20
  from .schemas import ActivityDetails, ActivitySummary, Sport, TraceDetails
21
21
  from .utils import decode_jwt_body
22
22
 
23
+
23
24
  AUTH_SUCCESSFUL_RESPONSE = "<!DOCTYPE html><html><body><h1>Authentication successful. You can now close this window.</h1></body></html>"
24
25
  OAUTH2_CLIENT_ID = "5382f68b0d254378"
25
- DEFAULT_URL = "https://app.sweatstack.no"
26
26
 
27
27
 
28
28
  class OAuth2Mixin:
@@ -0,0 +1 @@
1
+ DEFAULT_URL = "https://app.sweatstack.no"
@@ -0,0 +1,116 @@
1
+ import hashlib
2
+ import base64
3
+ import os
4
+ import secrets
5
+ import urllib.parse
6
+
7
+ try:
8
+ import streamlit as st
9
+ except ImportError:
10
+ raise ImportError(
11
+ "Streamlit features require streamlit to be installed. "
12
+ "You can install it with:\n\n"
13
+ "pip install 'sweatstack[streamlit]'\n\n"
14
+ )
15
+ import httpx
16
+ from streamlit_cookies_controller import CookieController
17
+ from sweatstack import Client
18
+
19
+ from .constants import DEFAULT_URL
20
+
21
+
22
+ cookie_controller = CookieController()
23
+
24
+
25
+ class StreamlitAuth:
26
+ def __init__(self, set_env_var=False, client_id=None, client_secret=None, scope=None, redirect_uri=None):
27
+ """
28
+ Args:
29
+ set_env_var: Whether to set the SWEATSTACK_API_KEY environment variable. Default is False.
30
+ client_id: The client ID to use. If not provided, the SWEATSTACK_CLIENT_ID environment variable will be used.
31
+ client_secret: The client secret to use. If not provided, the SWEATSTACK_CLIENT_SECRET environment variable will be used.
32
+ scope: The scope to use. If not provided, the SWEATSTACK_SCOPE environment variable will be used.
33
+ redirect_uri: The redirect URI to use. If not provided, the SWEATSTACK_REDIRECT_URI environment variable will be used.
34
+ """
35
+ self.set_env_var = set_env_var
36
+ self.client_id = client_id or os.environ.get("SWEATSTACK_CLIENT_ID")
37
+ self.client_secret = client_secret or os.environ.get("SWEATSTACK_CLIENT_SECRET")
38
+ self.scope = scope or os.environ.get("SWEATSTACK_SCOPE")
39
+ self.redirect_uri = redirect_uri or os.environ.get("SWEATSTACK_REDIRECT_URI")
40
+
41
+ self.api_key = cookie_controller.get("sweatstack_api_key")
42
+ self.client = Client(self.api_key)
43
+
44
+ if self.api_key and self.set_env_var:
45
+ os.environ["SWEATSTACK_API_KEY"] = self.api_key
46
+
47
+ def _show_sweatstack_logout(self):
48
+ if st.button("Logout"):
49
+ self.api_key = None
50
+ self.client = Client()
51
+ cookie_controller.remove("sweatstack_api_key")
52
+ if self.set_env_var:
53
+ os.environ.pop("SWEATSTACK_API_KEY")
54
+
55
+ def _show_sweatstack_login(self):
56
+ st.link_button("Login", self._get_authorization_url_implicit())
57
+
58
+ def _get_authorization_url_implicit(self):
59
+ code_verifier = secrets.token_urlsafe(32)
60
+ cookie_controller.set("code_verifier", code_verifier)
61
+ code_challenge = hashlib.sha256(code_verifier.encode("ascii")).digest()
62
+ code_challenge = base64.urlsafe_b64encode(code_challenge).rstrip(b"=").decode("ascii")
63
+
64
+ params = {
65
+ "client_id": self.client_id,
66
+ "redirect_uri": self.redirect_uri,
67
+ "code_challenge": code_challenge,
68
+ "scope": "data:read",
69
+ }
70
+ path = "/oauth/authorize"
71
+ authorization_url = urllib.parse.urljoin(DEFAULT_URL, path + "?" + urllib.parse.urlencode(params))
72
+
73
+ return authorization_url
74
+
75
+
76
+ def _exchange_token_implicit(self, code):
77
+ code_verifier = cookie_controller.get("code_verifier")
78
+ token_data = {
79
+ "grant_type": "authorization_code",
80
+ "client_id": self.client_id,
81
+ "code": code,
82
+ "code_verifier": code_verifier
83
+ }
84
+ response = httpx.post(
85
+ f"{DEFAULT_URL}/oauth/token",
86
+ data=token_data,
87
+ )
88
+ try:
89
+ response.raise_for_status()
90
+ except httpx.HTTPStatusError as e:
91
+ raise Exception(f"SweatStack Python login failed. Please try again.") from e
92
+ token_response = response.json()
93
+
94
+ self.api_key = token_response.get("access_token")
95
+ cookie_controller.set("sweatstack_api_key", self.api_key)
96
+ if self.set_env_var:
97
+ os.environ["SWEATSTACK_API_KEY"] = self.api_key
98
+
99
+ self.client = Client(self.api_key)
100
+
101
+ cookie_controller.remove("code_verifier")
102
+
103
+ return
104
+
105
+ def is_authenticated(self):
106
+ return self.api_key is not None
107
+
108
+ def authenticate(self):
109
+ if self.is_authenticated():
110
+ self._show_sweatstack_logout()
111
+ elif code := st.query_params.get("code"):
112
+ self._exchange_token_implicit(code)
113
+ st.query_params.clear()
114
+ st.rerun()
115
+ else:
116
+ self._show_sweatstack_login()
@@ -5,6 +5,22 @@ resolution-markers = [
5
5
  "python_full_version >= '4.0'",
6
6
  ]
7
7
 
8
+ [[package]]
9
+ name = "altair"
10
+ version = "5.5.0"
11
+ source = { registry = "https://pypi.org/simple" }
12
+ dependencies = [
13
+ { name = "jinja2" },
14
+ { name = "jsonschema" },
15
+ { name = "narwhals" },
16
+ { name = "packaging" },
17
+ { name = "typing-extensions", marker = "python_full_version < '3.14'" },
18
+ ]
19
+ sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305 }
20
+ wheels = [
21
+ { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200 },
22
+ ]
23
+
8
24
  [[package]]
9
25
  name = "annotated-types"
10
26
  version = "0.7.0"
@@ -181,6 +197,24 @@ css = [
181
197
  { name = "tinycss2" },
182
198
  ]
183
199
 
200
+ [[package]]
201
+ name = "blinker"
202
+ version = "1.9.0"
203
+ source = { registry = "https://pypi.org/simple" }
204
+ sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 }
205
+ wheels = [
206
+ { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 },
207
+ ]
208
+
209
+ [[package]]
210
+ name = "cachetools"
211
+ version = "5.5.1"
212
+ source = { registry = "https://pypi.org/simple" }
213
+ sdist = { url = "https://files.pythonhosted.org/packages/d9/74/57df1ab0ce6bc5f6fa868e08de20df8ac58f9c44330c7671ad922d2bbeae/cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95", size = 28044 }
214
+ wheels = [
215
+ { url = "https://files.pythonhosted.org/packages/ec/4e/de4ff18bcf55857ba18d3a4bd48c8a9fde6bb0980c9d20b263f05387fd88/cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb", size = 9530 },
216
+ ]
217
+
184
218
  [[package]]
185
219
  name = "certifi"
186
220
  version = "2024.12.14"
@@ -479,6 +513,30 @@ wheels = [
479
513
  { url = "https://files.pythonhosted.org/packages/f8/5c/e226de133afd8bb267ec27eead9ae3d784b95b39a287ed404caab39a5f50/genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7", size = 21470 },
480
514
  ]
481
515
 
516
+ [[package]]
517
+ name = "gitdb"
518
+ version = "4.0.12"
519
+ source = { registry = "https://pypi.org/simple" }
520
+ dependencies = [
521
+ { name = "smmap" },
522
+ ]
523
+ sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 }
524
+ wheels = [
525
+ { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 },
526
+ ]
527
+
528
+ [[package]]
529
+ name = "gitpython"
530
+ version = "3.1.44"
531
+ source = { registry = "https://pypi.org/simple" }
532
+ dependencies = [
533
+ { name = "gitdb" },
534
+ ]
535
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 }
536
+ wheels = [
537
+ { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 },
538
+ ]
539
+
482
540
  [[package]]
483
541
  name = "h11"
484
542
  version = "0.14.0"
@@ -885,6 +943,18 @@ wheels = [
885
943
  { url = "https://files.pythonhosted.org/packages/4c/fa/be89a49c640930180657482a74970cdcf6f7072c8d2471e1babe17a222dc/kiwisolver-1.4.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:be4816dc51c8a471749d664161b434912eee82f2ea66bd7628bd14583a833e85", size = 2349213 },
886
944
  ]
887
945
 
946
+ [[package]]
947
+ name = "markdown-it-py"
948
+ version = "3.0.0"
949
+ source = { registry = "https://pypi.org/simple" }
950
+ dependencies = [
951
+ { name = "mdurl" },
952
+ ]
953
+ sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 }
954
+ wheels = [
955
+ { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
956
+ ]
957
+
888
958
  [[package]]
889
959
  name = "markupsafe"
890
960
  version = "3.0.2"
@@ -972,6 +1042,15 @@ wheels = [
972
1042
  { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 },
973
1043
  ]
974
1044
 
1045
+ [[package]]
1046
+ name = "mdurl"
1047
+ version = "0.1.2"
1048
+ source = { registry = "https://pypi.org/simple" }
1049
+ sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 }
1050
+ wheels = [
1051
+ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 },
1052
+ ]
1053
+
975
1054
  [[package]]
976
1055
  name = "mistune"
977
1056
  version = "3.1.0"
@@ -990,6 +1069,15 @@ wheels = [
990
1069
  { url = "https://files.pythonhosted.org/packages/2a/e2/5d3f6ada4297caebe1a2add3b126fe800c96f56dbe5d1988a2cbe0b267aa/mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", size = 4695 },
991
1070
  ]
992
1071
 
1072
+ [[package]]
1073
+ name = "narwhals"
1074
+ version = "1.26.0"
1075
+ source = { registry = "https://pypi.org/simple" }
1076
+ sdist = { url = "https://files.pythonhosted.org/packages/18/6f/75929abaac73088fe34c788ecb40db20252174bcd00b8612381aebb954ee/narwhals-1.26.0.tar.gz", hash = "sha256:b9d7605bf1d97a9d87783a69748c39150964e2a1ab0e5a6fef3e59e56772639e", size = 248933 }
1077
+ wheels = [
1078
+ { url = "https://files.pythonhosted.org/packages/15/fc/420680ad8b0cf81372eee7a213a7b7173ec5a628f0d5b2426047fe55c3b3/narwhals-1.26.0-py3-none-any.whl", hash = "sha256:4af8bbdea9e45638bb9a981568a8dfa880e40eb7dcf740d19fd32aea79223c6f", size = 306574 },
1079
+ ]
1080
+
993
1081
  [[package]]
994
1082
  name = "nbclient"
995
1083
  version = "0.10.2"
@@ -1263,6 +1351,20 @@ wheels = [
1263
1351
  { url = "https://files.pythonhosted.org/packages/e4/ea/d836f008d33151c7a1f62caf3d8dd782e4d15f6a43897f64480c2b8de2ad/prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198", size = 387816 },
1264
1352
  ]
1265
1353
 
1354
+ [[package]]
1355
+ name = "protobuf"
1356
+ version = "5.29.3"
1357
+ source = { registry = "https://pypi.org/simple" }
1358
+ sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 }
1359
+ wheels = [
1360
+ { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 },
1361
+ { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 },
1362
+ { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 },
1363
+ { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 },
1364
+ { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 },
1365
+ { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 },
1366
+ ]
1367
+
1266
1368
  [[package]]
1267
1369
  name = "psutil"
1268
1370
  version = "6.1.1"
@@ -1391,6 +1493,19 @@ wheels = [
1391
1493
  { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 },
1392
1494
  ]
1393
1495
 
1496
+ [[package]]
1497
+ name = "pydeck"
1498
+ version = "0.9.1"
1499
+ source = { registry = "https://pypi.org/simple" }
1500
+ dependencies = [
1501
+ { name = "jinja2" },
1502
+ { name = "numpy" },
1503
+ ]
1504
+ sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 }
1505
+ wheels = [
1506
+ { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 },
1507
+ ]
1508
+
1394
1509
  [[package]]
1395
1510
  name = "pygments"
1396
1511
  version = "2.19.1"
@@ -1582,6 +1697,19 @@ wheels = [
1582
1697
  { url = "https://files.pythonhosted.org/packages/9e/51/17023c0f8f1869d8806b979a2bffa3f861f26a3f1a66b094288323fba52f/rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9", size = 4242 },
1583
1698
  ]
1584
1699
 
1700
+ [[package]]
1701
+ name = "rich"
1702
+ version = "13.9.4"
1703
+ source = { registry = "https://pypi.org/simple" }
1704
+ dependencies = [
1705
+ { name = "markdown-it-py" },
1706
+ { name = "pygments" },
1707
+ ]
1708
+ sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 }
1709
+ wheels = [
1710
+ { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 },
1711
+ ]
1712
+
1585
1713
  [[package]]
1586
1714
  name = "rpds-py"
1587
1715
  version = "0.22.3"
@@ -1656,6 +1784,15 @@ wheels = [
1656
1784
  { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
1657
1785
  ]
1658
1786
 
1787
+ [[package]]
1788
+ name = "smmap"
1789
+ version = "5.0.2"
1790
+ source = { registry = "https://pypi.org/simple" }
1791
+ sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 }
1792
+ wheels = [
1793
+ { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 },
1794
+ ]
1795
+
1659
1796
  [[package]]
1660
1797
  name = "sniffio"
1661
1798
  version = "1.3.1"
@@ -1688,9 +1825,51 @@ wheels = [
1688
1825
  { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 },
1689
1826
  ]
1690
1827
 
1828
+ [[package]]
1829
+ name = "streamlit"
1830
+ version = "1.42.0"
1831
+ source = { registry = "https://pypi.org/simple" }
1832
+ dependencies = [
1833
+ { name = "altair" },
1834
+ { name = "blinker" },
1835
+ { name = "cachetools" },
1836
+ { name = "click" },
1837
+ { name = "gitpython" },
1838
+ { name = "numpy" },
1839
+ { name = "packaging" },
1840
+ { name = "pandas" },
1841
+ { name = "pillow" },
1842
+ { name = "protobuf" },
1843
+ { name = "pyarrow" },
1844
+ { name = "pydeck" },
1845
+ { name = "requests" },
1846
+ { name = "rich" },
1847
+ { name = "tenacity" },
1848
+ { name = "toml" },
1849
+ { name = "tornado" },
1850
+ { name = "typing-extensions" },
1851
+ { name = "watchdog", marker = "sys_platform != 'darwin'" },
1852
+ ]
1853
+ sdist = { url = "https://files.pythonhosted.org/packages/0e/61/a59da06a2822b265fc6230650a1ec4b9203da76ee9cf023083752debf9fa/streamlit-1.42.0.tar.gz", hash = "sha256:8c48494ccfad33e7d0bc5873151800b203cb71203bfd42bc7418940710ca4970", size = 9169261 }
1854
+ wheels = [
1855
+ { url = "https://files.pythonhosted.org/packages/ad/dc/69068179e09488d0833a970d06e8bf40e35669a7bddb8a3caadc13b7dff4/streamlit-1.42.0-py2.py3-none-any.whl", hash = "sha256:edf333fd3525b7c64b19e1156b483a1a93cbdb09a3a06f26478388d68f971090", size = 9560180 },
1856
+ ]
1857
+
1858
+ [[package]]
1859
+ name = "streamlit-cookies-controller"
1860
+ version = "0.0.4"
1861
+ source = { registry = "https://pypi.org/simple" }
1862
+ dependencies = [
1863
+ { name = "streamlit" },
1864
+ ]
1865
+ sdist = { url = "https://files.pythonhosted.org/packages/b0/c8/7caf505d62faeb8c0f6e6b74b554e1c2b0c3ca46c0a39250445c574a5a18/streamlit-cookies-controller-0.0.4.tar.gz", hash = "sha256:f5df2543de858f1585dcb6e90f58f38aafbf313ee8e237c64663b2d9761583cc", size = 406305 }
1866
+ wheels = [
1867
+ { url = "https://files.pythonhosted.org/packages/02/c6/3bbdfecf5009943bcb62d37e3fd4b3fe45e87137e449a50e3025170bc9f7/streamlit_cookies_controller-0.0.4-py3-none-any.whl", hash = "sha256:f97fec6acdeee9cb9e16da25c3fc91d404b5b0ddced87c1d9fa9c62f65ca3251", size = 409303 },
1868
+ ]
1869
+
1691
1870
  [[package]]
1692
1871
  name = "sweatstack"
1693
- version = "0.1.0"
1872
+ version = "0.12.0"
1694
1873
  source = { editable = "." }
1695
1874
  dependencies = [
1696
1875
  { name = "httpx" },
@@ -1705,6 +1884,10 @@ jupyterlab = [
1705
1884
  { name = "jupyterlab" },
1706
1885
  { name = "matplotlib" },
1707
1886
  ]
1887
+ streamlit = [
1888
+ { name = "streamlit" },
1889
+ { name = "streamlit-cookies-controller" },
1890
+ ]
1708
1891
 
1709
1892
  [package.dev-dependencies]
1710
1893
  dev = [
@@ -1720,11 +1903,22 @@ requires-dist = [
1720
1903
  { name = "pandas", specifier = ">=2.2.3" },
1721
1904
  { name = "pyarrow", specifier = ">=19.0.0" },
1722
1905
  { name = "pydantic", specifier = ">=2.10.5" },
1906
+ { name = "streamlit", marker = "extra == 'streamlit'", specifier = ">=1.42.0" },
1907
+ { name = "streamlit-cookies-controller", marker = "extra == 'streamlit'", specifier = ">=0.0.4" },
1723
1908
  ]
1724
1909
 
1725
1910
  [package.metadata.requires-dev]
1726
1911
  dev = [{ name = "datamodel-code-generator", specifier = ">=0.26.5" }]
1727
1912
 
1913
+ [[package]]
1914
+ name = "tenacity"
1915
+ version = "9.0.0"
1916
+ source = { registry = "https://pypi.org/simple" }
1917
+ sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 }
1918
+ wheels = [
1919
+ { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 },
1920
+ ]
1921
+
1728
1922
  [[package]]
1729
1923
  name = "terminado"
1730
1924
  version = "0.18.1"
@@ -1751,6 +1945,15 @@ wheels = [
1751
1945
  { url = "https://files.pythonhosted.org/packages/e6/34/ebdc18bae6aa14fbee1a08b63c015c72b64868ff7dae68808ab500c492e2/tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289", size = 26610 },
1752
1946
  ]
1753
1947
 
1948
+ [[package]]
1949
+ name = "toml"
1950
+ version = "0.10.2"
1951
+ source = { registry = "https://pypi.org/simple" }
1952
+ sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 }
1953
+ wheels = [
1954
+ { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 },
1955
+ ]
1956
+
1754
1957
  [[package]]
1755
1958
  name = "tornado"
1756
1959
  version = "6.4.2"
@@ -1823,6 +2026,24 @@ wheels = [
1823
2026
  { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 },
1824
2027
  ]
1825
2028
 
2029
+ [[package]]
2030
+ name = "watchdog"
2031
+ version = "6.0.0"
2032
+ source = { registry = "https://pypi.org/simple" }
2033
+ sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 }
2034
+ wheels = [
2035
+ { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 },
2036
+ { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 },
2037
+ { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 },
2038
+ { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 },
2039
+ { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 },
2040
+ { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 },
2041
+ { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 },
2042
+ { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 },
2043
+ { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 },
2044
+ { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 },
2045
+ ]
2046
+
1826
2047
  [[package]]
1827
2048
  name = "wcwidth"
1828
2049
  version = "0.2.13"
File without changes
File without changes
File without changes
File without changes