sweatstack 0.55.0__tar.gz → 0.57.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.
- {sweatstack-0.55.0 → sweatstack-0.57.0}/CHANGELOG.md +12 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/PKG-INFO +1 -1
- {sweatstack-0.55.0 → sweatstack-0.57.0}/docs/everything.rst +91 -2
- {sweatstack-0.55.0 → sweatstack-0.57.0}/pyproject.toml +1 -1
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/client.py +34 -10
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/streamlit.py +126 -37
- {sweatstack-0.55.0 → sweatstack-0.57.0}/.claude/settings.local.json +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/.gitignore +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/.python-version +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/DEVELOPMENT.md +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/Makefile +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/README.md +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/docs/conf.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/docs/index.rst +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/.ipynb_checkpoints/Untitled-checkpoint.ipynb +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/README.md +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/Sweat Stack examples/Getting started.ipynb +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/Untitled.ipynb +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/hello.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/playground/pyproject.toml +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/__init__.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/cli.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/constants.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/ipython_init.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/jupyterlab_oauth2_startup.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/openapi_schemas.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/py.typed +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/schemas.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/sweatshell.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/utils.py +0 -0
- {sweatstack-0.55.0 → sweatstack-0.57.0}/uv.lock +0 -0
|
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
8
|
|
|
9
|
+
## [0.57.0] - 2025-12-04
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Added a new "proxy mode" to the `ss.StreamlitAuth` class that allows running Streamlit apps behind a proxy. The proxy mode is enabled by calling `ss.StreamlitAuth.behind_proxy()`. The proxy should handle the OAuth callback and token exchange and pass the access token to the app via the `X-SweatStack-Token` (configurable) header. The OAuth2 flow is still initiated by the `ss.StreamlitAuth` class.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## [0.56.0] - 2025-11-21
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- Fixed an issue where refreshing the token would not succeed with the Streamlit integration.
|
|
19
|
+
|
|
20
|
+
|
|
9
21
|
## [0.55.0] - 2025-10-24
|
|
10
22
|
|
|
11
23
|
### Added
|
|
@@ -163,9 +163,98 @@ Metric
|
|
|
163
163
|
:undoc-members:
|
|
164
164
|
|
|
165
165
|
sweatstack.openapi_schemas
|
|
166
|
-
|
|
166
|
+
--------------------------
|
|
167
|
+
|
|
168
|
+
Core Data Models
|
|
169
|
+
~~~~~~~~~~~~~~~~
|
|
170
|
+
|
|
171
|
+
.. autoclass:: sweatstack.openapi_schemas.ActivitySummary
|
|
172
|
+
:members:
|
|
173
|
+
:undoc-members:
|
|
174
|
+
:show-inheritance:
|
|
175
|
+
|
|
176
|
+
.. autoclass:: sweatstack.openapi_schemas.ActivityDetails
|
|
177
|
+
:members:
|
|
178
|
+
:undoc-members:
|
|
179
|
+
:show-inheritance:
|
|
180
|
+
|
|
181
|
+
.. autoclass:: sweatstack.openapi_schemas.TraceDetails
|
|
182
|
+
:members:
|
|
183
|
+
:undoc-members:
|
|
184
|
+
:show-inheritance:
|
|
185
|
+
|
|
186
|
+
.. autoclass:: sweatstack.openapi_schemas.Lap
|
|
187
|
+
:members:
|
|
188
|
+
:undoc-members:
|
|
189
|
+
:show-inheritance:
|
|
190
|
+
|
|
191
|
+
Activity Summaries
|
|
192
|
+
~~~~~~~~~~~~~~~~~~
|
|
193
|
+
|
|
194
|
+
.. autoclass:: sweatstack.openapi_schemas.ActivitySummarySummary
|
|
195
|
+
:members:
|
|
196
|
+
:undoc-members:
|
|
197
|
+
:show-inheritance:
|
|
198
|
+
|
|
199
|
+
.. autoclass:: sweatstack.openapi_schemas.PowerSummary
|
|
200
|
+
:members:
|
|
201
|
+
:undoc-members:
|
|
202
|
+
:show-inheritance:
|
|
203
|
+
|
|
204
|
+
.. autoclass:: sweatstack.openapi_schemas.SpeedSummary
|
|
205
|
+
:members:
|
|
206
|
+
:undoc-members:
|
|
207
|
+
:show-inheritance:
|
|
208
|
+
|
|
209
|
+
.. autoclass:: sweatstack.openapi_schemas.DistanceSummary
|
|
210
|
+
:members:
|
|
211
|
+
:undoc-members:
|
|
212
|
+
:show-inheritance:
|
|
213
|
+
|
|
214
|
+
.. autoclass:: sweatstack.openapi_schemas.ElevationSummary
|
|
215
|
+
:members:
|
|
216
|
+
:undoc-members:
|
|
217
|
+
:show-inheritance:
|
|
218
|
+
|
|
219
|
+
.. autoclass:: sweatstack.openapi_schemas.HeartRateSummary
|
|
220
|
+
:members:
|
|
221
|
+
:undoc-members:
|
|
222
|
+
:show-inheritance:
|
|
223
|
+
|
|
224
|
+
.. autoclass:: sweatstack.openapi_schemas.TemperatureSummary
|
|
225
|
+
:members:
|
|
226
|
+
:undoc-members:
|
|
227
|
+
:show-inheritance:
|
|
228
|
+
|
|
229
|
+
.. autoclass:: sweatstack.openapi_schemas.CoreTemperatureSummary
|
|
230
|
+
:members:
|
|
231
|
+
:undoc-members:
|
|
232
|
+
:show-inheritance:
|
|
233
|
+
|
|
234
|
+
.. autoclass:: sweatstack.openapi_schemas.Smo2Summary
|
|
235
|
+
:members:
|
|
236
|
+
:undoc-members:
|
|
237
|
+
:show-inheritance:
|
|
238
|
+
|
|
239
|
+
User & Authentication
|
|
240
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
241
|
+
|
|
242
|
+
.. autoclass:: sweatstack.openapi_schemas.UserSummary
|
|
243
|
+
:members:
|
|
244
|
+
:undoc-members:
|
|
245
|
+
:show-inheritance:
|
|
246
|
+
|
|
247
|
+
.. autoclass:: sweatstack.openapi_schemas.UserInfoResponse
|
|
248
|
+
:members:
|
|
249
|
+
:undoc-members:
|
|
250
|
+
:show-inheritance:
|
|
251
|
+
|
|
252
|
+
.. autoclass:: sweatstack.openapi_schemas.TokenResponse
|
|
253
|
+
:members:
|
|
254
|
+
:undoc-members:
|
|
255
|
+
:show-inheritance:
|
|
167
256
|
|
|
168
|
-
..
|
|
257
|
+
.. autoclass:: sweatstack.openapi_schemas.BackfillStatus
|
|
169
258
|
:members:
|
|
170
259
|
:undoc-members:
|
|
171
260
|
:show-inheritance:
|
|
@@ -669,6 +669,8 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
|
|
|
669
669
|
refresh_token: str | None = None,
|
|
670
670
|
url: str | None = None,
|
|
671
671
|
streamlit_compatible: bool = False,
|
|
672
|
+
client_id: str | None = None,
|
|
673
|
+
client_secret: str | None = None,
|
|
672
674
|
):
|
|
673
675
|
"""Initialize a SweatStack client.
|
|
674
676
|
|
|
@@ -682,18 +684,28 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
|
|
|
682
684
|
self.refresh_token = refresh_token
|
|
683
685
|
self.url = url
|
|
684
686
|
self.streamlit_compatible = streamlit_compatible
|
|
687
|
+
self.client_id = client_id or OAUTH2_CLIENT_ID
|
|
688
|
+
self.client_secret = client_secret
|
|
685
689
|
|
|
686
690
|
def _do_token_refresh(self, tz: str) -> str:
|
|
687
|
-
|
|
691
|
+
refresh_token = self.refresh_token
|
|
692
|
+
if refresh_token is None:
|
|
693
|
+
raise ValueError(
|
|
694
|
+
"Cannot refresh token: no refresh_token available. "
|
|
695
|
+
"If using Streamlit, ensure you're using StreamlitAuth which handles token refresh automatically."
|
|
696
|
+
)
|
|
697
|
+
|
|
698
|
+
with self._http_client(skip_token_check=True) as client:
|
|
688
699
|
response = client.post(
|
|
689
700
|
"/api/v1/oauth/token",
|
|
690
|
-
|
|
701
|
+
data={
|
|
691
702
|
"grant_type": "refresh_token",
|
|
692
|
-
"refresh_token":
|
|
703
|
+
"refresh_token": refresh_token,
|
|
693
704
|
"tz": tz,
|
|
705
|
+
"client_id": self.client_id,
|
|
706
|
+
"client_secret": self.client_secret,
|
|
694
707
|
},
|
|
695
708
|
)
|
|
696
|
-
|
|
697
709
|
self._raise_for_status(response)
|
|
698
710
|
return response.json()["access_token"]
|
|
699
711
|
|
|
@@ -701,12 +713,13 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
|
|
|
701
713
|
try:
|
|
702
714
|
body = decode_jwt_body(token)
|
|
703
715
|
# Margin in seconds to account for time to token validation of the next request
|
|
704
|
-
TOKEN_EXPIRY_MARGIN = 5
|
|
716
|
+
TOKEN_EXPIRY_MARGIN = 5 # 5 seconds. Meaning that if the token is within 5 seconds of expiring, it will be refreshed.
|
|
705
717
|
if body["exp"] - TOKEN_EXPIRY_MARGIN < time.time():
|
|
706
718
|
# Token is (almost) expired, refresh it
|
|
707
719
|
token = self._do_token_refresh(body["tz"])
|
|
708
720
|
self._api_key = token
|
|
709
|
-
except Exception:
|
|
721
|
+
except Exception as exception:
|
|
722
|
+
logging.warning("Exception checking token expiry: %s", exception)
|
|
710
723
|
# If token can't be decoded, just return as-is
|
|
711
724
|
# @TODO: This probably should be handled differently
|
|
712
725
|
pass
|
|
@@ -776,16 +789,27 @@ class Client(_OAuth2Mixin, _DelegationMixin, _TokenStorageMixin, _LocalCacheMixi
|
|
|
776
789
|
self._url = value
|
|
777
790
|
|
|
778
791
|
@contextlib.contextmanager
|
|
779
|
-
def _http_client(self):
|
|
792
|
+
def _http_client(self, skip_token_check: bool = False):
|
|
780
793
|
"""
|
|
781
794
|
Creates an httpx client with the base URL and authentication headers pre-configured.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
skip_token_check: If True, uses the raw _api_key without triggering token expiry check.
|
|
798
|
+
This prevents recursive token refresh attempts.
|
|
782
799
|
"""
|
|
783
800
|
headers = {
|
|
784
801
|
"User-Agent": f"python-sweatstack/{__version__}",
|
|
785
802
|
}
|
|
786
|
-
if
|
|
787
|
-
|
|
788
|
-
|
|
803
|
+
if skip_token_check:
|
|
804
|
+
# Use raw token without triggering expiry check (used during refresh)
|
|
805
|
+
token = self._api_key
|
|
806
|
+
else:
|
|
807
|
+
# Normal path: may trigger token refresh
|
|
808
|
+
token = self.api_key
|
|
809
|
+
|
|
810
|
+
if token:
|
|
811
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
812
|
+
|
|
789
813
|
with httpx.Client(base_url=self.url, headers=headers, timeout=60) as client:
|
|
790
814
|
yield client
|
|
791
815
|
|
|
@@ -114,19 +114,115 @@ class StreamlitAuth:
|
|
|
114
114
|
|
|
115
115
|
self.redirect_uri = redirect_uri or os.environ.get("SWEATSTACK_REDIRECT_URI")
|
|
116
116
|
|
|
117
|
+
self._proxy_mode = False
|
|
118
|
+
self._logout_uri = None
|
|
119
|
+
|
|
117
120
|
self.api_key = st.session_state.get("sweatstack_api_key")
|
|
118
|
-
self.
|
|
121
|
+
self.refresh_token = st.session_state.get("sweatstack_refresh_token")
|
|
122
|
+
self.client = Client(
|
|
123
|
+
self.api_key,
|
|
124
|
+
refresh_token=self.refresh_token,
|
|
125
|
+
streamlit_compatible=True,
|
|
126
|
+
client_id=self.client_id,
|
|
127
|
+
client_secret=self.client_secret,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@classmethod
|
|
131
|
+
def behind_proxy(
|
|
132
|
+
cls,
|
|
133
|
+
redirect_uri: str,
|
|
134
|
+
header_name: str = "X-SweatStack-Token",
|
|
135
|
+
logout_uri: str = "/logout",
|
|
136
|
+
) -> "StreamlitAuth":
|
|
137
|
+
"""Create a StreamlitAuth instance for use behind a proxy.
|
|
138
|
+
|
|
139
|
+
Use this method when your Streamlit app runs behind a proxy that handles
|
|
140
|
+
authentication and passes the SweatStack access token via an HTTP header.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
redirect_uri: The URI to redirect to after login (used by proxy).
|
|
144
|
+
header_name: The HTTP header name containing the access token.
|
|
145
|
+
Defaults to "X-SweatStack-Token".
|
|
146
|
+
logout_uri: The URI to redirect to for logout.
|
|
147
|
+
Defaults to "/logout".
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
StreamlitAuth: An instance configured for proxy mode.
|
|
151
|
+
|
|
152
|
+
Example:
|
|
153
|
+
auth = StreamlitAuth.behind_proxy(
|
|
154
|
+
redirect_uri="https://myapp.example.com/app",
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
if not auth.is_authenticated():
|
|
158
|
+
st.error("Missing authentication header")
|
|
159
|
+
st.stop()
|
|
160
|
+
|
|
161
|
+
activities = auth.client.get_activities()
|
|
162
|
+
"""
|
|
163
|
+
instance = cls(redirect_uri=redirect_uri)
|
|
164
|
+
instance._proxy_mode = True
|
|
165
|
+
instance._logout_uri = logout_uri
|
|
166
|
+
|
|
167
|
+
token = st.context.headers.get(header_name)
|
|
168
|
+
if token:
|
|
169
|
+
instance.api_key = token
|
|
170
|
+
instance.client = Client(token, streamlit_compatible=True)
|
|
171
|
+
|
|
172
|
+
return instance
|
|
173
|
+
|
|
174
|
+
def _show_styled_link_button(self, label: str, url: str):
|
|
175
|
+
"""Displays a styled link button with hover effects.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
label: Text to display on the button.
|
|
179
|
+
url: The URL to navigate to when clicked.
|
|
180
|
+
"""
|
|
181
|
+
st.markdown(
|
|
182
|
+
f"""
|
|
183
|
+
<style>
|
|
184
|
+
.animated-button {{
|
|
185
|
+
}}
|
|
186
|
+
.animated-button:hover {{
|
|
187
|
+
transform: scale(1.05);
|
|
188
|
+
}}
|
|
189
|
+
.animated-button:active {{
|
|
190
|
+
transform: scale(1);
|
|
191
|
+
}}
|
|
192
|
+
</style>
|
|
193
|
+
<a href="{url}"
|
|
194
|
+
target="_top"
|
|
195
|
+
class="animated-button"
|
|
196
|
+
style="display: inline-block;
|
|
197
|
+
padding: 10px 20px;
|
|
198
|
+
background-color: #EF2B2D;
|
|
199
|
+
color: white;
|
|
200
|
+
text-decoration: none;
|
|
201
|
+
border-radius: 6px;
|
|
202
|
+
border: none;
|
|
203
|
+
transition: all 0.3s ease;
|
|
204
|
+
cursor: pointer;"
|
|
205
|
+
>{label}</a>
|
|
206
|
+
""",
|
|
207
|
+
unsafe_allow_html=True,
|
|
208
|
+
)
|
|
119
209
|
|
|
120
210
|
def logout_button(self):
|
|
121
211
|
"""Displays a logout button and handles user logout.
|
|
122
212
|
|
|
123
|
-
|
|
124
|
-
resets the client, and triggers a Streamlit rerun
|
|
213
|
+
In standard mode, clears the stored API key from session state,
|
|
214
|
+
resets the client, and triggers a Streamlit rerun.
|
|
215
|
+
|
|
216
|
+
In proxy mode, displays a styled link that redirects to the logout URI.
|
|
125
217
|
"""
|
|
126
|
-
if
|
|
218
|
+
if self._proxy_mode:
|
|
219
|
+
self._show_styled_link_button("Logout", self._logout_uri)
|
|
220
|
+
elif st.button("Logout"):
|
|
127
221
|
self.api_key = None
|
|
222
|
+
self.refresh_token = None
|
|
128
223
|
self.client = Client(streamlit_compatible=True)
|
|
129
|
-
st.session_state.pop("sweatstack_api_key")
|
|
224
|
+
st.session_state.pop("sweatstack_api_key", None)
|
|
225
|
+
st.session_state.pop("sweatstack_refresh_token", None)
|
|
130
226
|
st.rerun()
|
|
131
227
|
|
|
132
228
|
def _running_on_streamlit_cloud(self):
|
|
@@ -142,34 +238,7 @@ class StreamlitAuth:
|
|
|
142
238
|
authorization_url = self.get_authorization_url()
|
|
143
239
|
login_label = login_label or "Connect with SweatStack"
|
|
144
240
|
if not self._running_on_streamlit_cloud():
|
|
145
|
-
|
|
146
|
-
f"""
|
|
147
|
-
<style>
|
|
148
|
-
.animated-button {{
|
|
149
|
-
}}
|
|
150
|
-
.animated-button:hover {{
|
|
151
|
-
transform: scale(1.05);
|
|
152
|
-
}}
|
|
153
|
-
.animated-button:active {{
|
|
154
|
-
transform: scale(1);
|
|
155
|
-
}}
|
|
156
|
-
</style>
|
|
157
|
-
<a href="{authorization_url}"
|
|
158
|
-
target="_top"
|
|
159
|
-
class="animated-button"
|
|
160
|
-
style="display: inline-block;
|
|
161
|
-
padding: 10px 20px;
|
|
162
|
-
background-color: #EF2B2D;
|
|
163
|
-
color: white;
|
|
164
|
-
text-decoration: none;
|
|
165
|
-
border-radius: 6px;
|
|
166
|
-
border: none;
|
|
167
|
-
transition: all 0.3s ease;
|
|
168
|
-
cursor: pointer;"
|
|
169
|
-
>{login_label}</a>
|
|
170
|
-
""",
|
|
171
|
-
unsafe_allow_html=True,
|
|
172
|
-
)
|
|
241
|
+
self._show_styled_link_button(login_label, authorization_url)
|
|
173
242
|
else:
|
|
174
243
|
st.link_button(login_label, authorization_url)
|
|
175
244
|
|
|
@@ -193,15 +262,21 @@ class StreamlitAuth:
|
|
|
193
262
|
|
|
194
263
|
return authorization_url
|
|
195
264
|
|
|
196
|
-
def _set_api_key(self, api_key):
|
|
197
|
-
"""Sets the API key in instance and session state, then refreshes the client.
|
|
265
|
+
def _set_api_key(self, api_key, refresh_token=None):
|
|
266
|
+
"""Sets the API key and refresh token in instance and session state, then refreshes the client.
|
|
198
267
|
|
|
199
268
|
Args:
|
|
200
269
|
api_key: The API access token to set.
|
|
270
|
+
refresh_token: The refresh token to set. If None, keeps the existing refresh token.
|
|
201
271
|
"""
|
|
202
272
|
self.api_key = api_key
|
|
203
273
|
st.session_state["sweatstack_api_key"] = api_key
|
|
204
|
-
|
|
274
|
+
|
|
275
|
+
if refresh_token is not None:
|
|
276
|
+
self.refresh_token = refresh_token
|
|
277
|
+
st.session_state["sweatstack_refresh_token"] = refresh_token
|
|
278
|
+
|
|
279
|
+
self.client = Client(self.api_key, refresh_token=self.refresh_token, streamlit_compatible=True)
|
|
205
280
|
|
|
206
281
|
def _exchange_token(self, code):
|
|
207
282
|
"""Exchanges an authorization code for an access token.
|
|
@@ -230,7 +305,10 @@ class StreamlitAuth:
|
|
|
230
305
|
raise Exception(f"SweatStack Python login failed. Please try again.") from e
|
|
231
306
|
token_response = response.json()
|
|
232
307
|
|
|
233
|
-
self._set_api_key(
|
|
308
|
+
self._set_api_key(
|
|
309
|
+
token_response.get("access_token"),
|
|
310
|
+
refresh_token=token_response.get("refresh_token")
|
|
311
|
+
)
|
|
234
312
|
|
|
235
313
|
return
|
|
236
314
|
|
|
@@ -257,12 +335,23 @@ class StreamlitAuth:
|
|
|
257
335
|
to the Streamlit app with an authorization code, which is exchanged for an
|
|
258
336
|
access token.
|
|
259
337
|
|
|
338
|
+
In proxy mode, this method only shows the login button if not authenticated.
|
|
339
|
+
The proxy handles the OAuth callback and token exchange.
|
|
340
|
+
|
|
260
341
|
Args:
|
|
261
342
|
login_label: The label to display on the login button. Defaults to "Login with SweatStack".
|
|
262
343
|
|
|
263
344
|
Returns:
|
|
264
345
|
None
|
|
265
346
|
"""
|
|
347
|
+
if self._proxy_mode:
|
|
348
|
+
if self.is_authenticated():
|
|
349
|
+
if show_logout:
|
|
350
|
+
self.logout_button()
|
|
351
|
+
else:
|
|
352
|
+
self._show_sweatstack_login(login_label)
|
|
353
|
+
return
|
|
354
|
+
|
|
266
355
|
if self.is_authenticated():
|
|
267
356
|
if not st.session_state.get("sweatstack_auth_toast_shown", False):
|
|
268
357
|
st.toast("SweatStack authentication successful!", icon="✅")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sweatstack-0.55.0 → sweatstack-0.57.0}/playground/.ipynb_checkpoints/Untitled-checkpoint.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
{sweatstack-0.55.0 → sweatstack-0.57.0}/playground/Sweat Stack examples/Getting started.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sweatstack-0.55.0 → sweatstack-0.57.0}/src/sweatstack/Sweat Stack examples/Getting started.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|