squirrels 0.5.0b3__py3-none-any.whl → 0.5.0b4__py3-none-any.whl
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.
Potentially problematic release.
This version of squirrels might be problematic. Click here for more details.
- squirrels/__init__.py +2 -0
- squirrels/_api_routes/__init__.py +5 -0
- squirrels/_api_routes/auth.py +262 -0
- squirrels/_api_routes/base.py +154 -0
- squirrels/_api_routes/dashboards.py +142 -0
- squirrels/_api_routes/data_management.py +103 -0
- squirrels/_api_routes/datasets.py +242 -0
- squirrels/_api_routes/oauth2.py +300 -0
- squirrels/_api_routes/project.py +214 -0
- squirrels/_api_server.py +142 -745
- squirrels/_arguments/__init__.py +0 -0
- squirrels/_arguments/{_init_time_args.py → init_time_args.py} +5 -0
- squirrels/_arguments/{_run_time_args.py → run_time_args.py} +1 -1
- squirrels/_auth.py +645 -92
- squirrels/_connection_set.py +1 -1
- squirrels/_constants.py +6 -0
- squirrels/{_dashboards_io.py → _dashboards.py} +87 -6
- squirrels/_exceptions.py +9 -37
- squirrels/_model_builder.py +1 -1
- squirrels/_model_queries.py +1 -1
- squirrels/_models.py +13 -12
- squirrels/_package_data/base_project/.env +1 -0
- squirrels/_package_data/base_project/.env.example +1 -0
- squirrels/_package_data/base_project/pyconfigs/parameters.py +84 -76
- squirrels/_package_data/base_project/pyconfigs/user.py +30 -2
- squirrels/_package_data/templates/dataset_results.html +112 -0
- squirrels/_package_data/templates/oauth_login.html +271 -0
- squirrels/_parameter_configs.py +1 -1
- squirrels/_parameter_sets.py +31 -21
- squirrels/_parameters.py +521 -123
- squirrels/_project.py +43 -24
- squirrels/_py_module.py +3 -2
- squirrels/_schemas/__init__.py +0 -0
- squirrels/_schemas/auth_models.py +144 -0
- squirrels/_schemas/query_param_models.py +67 -0
- squirrels/{_api_response_models.py → _schemas/response_models.py} +12 -8
- squirrels/_utils.py +34 -2
- squirrels/arguments.py +2 -2
- squirrels/auth.py +1 -0
- squirrels/dashboards.py +1 -1
- squirrels/types.py +3 -3
- {squirrels-0.5.0b3.dist-info → squirrels-0.5.0b4.dist-info}/METADATA +4 -1
- {squirrels-0.5.0b3.dist-info → squirrels-0.5.0b4.dist-info}/RECORD +46 -32
- squirrels/_dashboard_types.py +0 -82
- {squirrels-0.5.0b3.dist-info → squirrels-0.5.0b4.dist-info}/WHEEL +0 -0
- {squirrels-0.5.0b3.dist-info → squirrels-0.5.0b4.dist-info}/entry_points.txt +0 -0
- {squirrels-0.5.0b3.dist-info → squirrels-0.5.0b4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Login - OAuth Authorization</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
|
|
16
|
+
background: #f5f7fa;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
justify-content: center;
|
|
21
|
+
padding: 20px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.login-container {
|
|
25
|
+
background: white;
|
|
26
|
+
border-radius: 12px;
|
|
27
|
+
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
|
28
|
+
padding: 40px;
|
|
29
|
+
width: 100%;
|
|
30
|
+
max-width: 450px;
|
|
31
|
+
position: relative;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.logo {
|
|
35
|
+
text-align: center;
|
|
36
|
+
margin-bottom: 30px;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.logo h1 {
|
|
40
|
+
color: #1f2937;
|
|
41
|
+
font-size: 24px;
|
|
42
|
+
font-weight: 700;
|
|
43
|
+
margin-bottom: 8px;
|
|
44
|
+
line-height: 1.3;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.logo .brand {
|
|
48
|
+
color: #1f2937;
|
|
49
|
+
font-weight: 700;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.logo .project {
|
|
53
|
+
color: #6b7280;
|
|
54
|
+
font-weight: 600;
|
|
55
|
+
font-size: 20px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.logo p {
|
|
59
|
+
color: #6b7280;
|
|
60
|
+
font-size: 14px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.client-info {
|
|
64
|
+
background: #f8f9fa;
|
|
65
|
+
border-radius: 8px;
|
|
66
|
+
padding: 16px;
|
|
67
|
+
margin-bottom: 24px;
|
|
68
|
+
border-left: 4px solid #2563eb;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.client-info h3 {
|
|
72
|
+
color: #333;
|
|
73
|
+
font-size: 16px;
|
|
74
|
+
margin-bottom: 4px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.client-info p {
|
|
78
|
+
color: #666;
|
|
79
|
+
font-size: 14px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.form-group {
|
|
83
|
+
margin-bottom: 20px;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.form-group label {
|
|
87
|
+
display: block;
|
|
88
|
+
margin-bottom: 6px;
|
|
89
|
+
color: #333;
|
|
90
|
+
font-weight: 500;
|
|
91
|
+
font-size: 14px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.form-group input {
|
|
95
|
+
width: 100%;
|
|
96
|
+
padding: 12px 16px;
|
|
97
|
+
border: 2px solid #e1e5e9;
|
|
98
|
+
border-radius: 8px;
|
|
99
|
+
font-size: 16px;
|
|
100
|
+
transition: border-color 0.3s ease;
|
|
101
|
+
background-color: #fff;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.form-group input:focus {
|
|
105
|
+
outline: none;
|
|
106
|
+
border-color: #2563eb;
|
|
107
|
+
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.btn {
|
|
111
|
+
width: 100%;
|
|
112
|
+
padding: 12px 16px;
|
|
113
|
+
border: none;
|
|
114
|
+
border-radius: 8px;
|
|
115
|
+
font-size: 16px;
|
|
116
|
+
font-weight: 600;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
transition: all 0.3s ease;
|
|
119
|
+
text-decoration: none;
|
|
120
|
+
display: inline-block;
|
|
121
|
+
text-align: center;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.btn-primary {
|
|
125
|
+
background: #2563eb;
|
|
126
|
+
color: white;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.btn-primary:hover {
|
|
130
|
+
background: #1d4ed8;
|
|
131
|
+
transform: translateY(-1px);
|
|
132
|
+
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.btn-provider {
|
|
136
|
+
background: #fff;
|
|
137
|
+
color: #333;
|
|
138
|
+
border: 2px solid #e1e5e9;
|
|
139
|
+
margin-bottom: 12px;
|
|
140
|
+
display: flex;
|
|
141
|
+
align-items: center;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
gap: 12px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.btn-provider:hover {
|
|
147
|
+
border-color: #2563eb;
|
|
148
|
+
background: #f8f9fa;
|
|
149
|
+
transform: translateY(-1px);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.provider-icon {
|
|
153
|
+
width: 20px;
|
|
154
|
+
height: 20px;
|
|
155
|
+
border-radius: 50%;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.divider {
|
|
159
|
+
text-align: center;
|
|
160
|
+
margin: 24px 0;
|
|
161
|
+
position: relative;
|
|
162
|
+
color: #666;
|
|
163
|
+
font-size: 14px;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.divider::before {
|
|
167
|
+
content: '';
|
|
168
|
+
position: absolute;
|
|
169
|
+
top: 50%;
|
|
170
|
+
left: 0;
|
|
171
|
+
right: 0;
|
|
172
|
+
height: 1px;
|
|
173
|
+
background: #e1e5e9;
|
|
174
|
+
z-index: 1;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.divider span {
|
|
178
|
+
background: white;
|
|
179
|
+
padding: 0 16px;
|
|
180
|
+
position: relative;
|
|
181
|
+
z-index: 2;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.error-message {
|
|
185
|
+
background: #fee;
|
|
186
|
+
border: 1px solid #fcc;
|
|
187
|
+
color: #c33;
|
|
188
|
+
padding: 12px;
|
|
189
|
+
border-radius: 8px;
|
|
190
|
+
margin-bottom: 20px;
|
|
191
|
+
font-size: 14px;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.info-message {
|
|
195
|
+
background: #e3f2fd;
|
|
196
|
+
border: 1px solid #bbdefb;
|
|
197
|
+
color: #1976d2;
|
|
198
|
+
padding: 12px;
|
|
199
|
+
border-radius: 8px;
|
|
200
|
+
margin-bottom: 20px;
|
|
201
|
+
font-size: 14px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@media (max-width: 480px) {
|
|
205
|
+
.login-container {
|
|
206
|
+
padding: 24px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.logo h1 {
|
|
210
|
+
font-size: 24px;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
</style>
|
|
214
|
+
</head>
|
|
215
|
+
<body>
|
|
216
|
+
<div class="login-container">
|
|
217
|
+
<div class="logo">
|
|
218
|
+
<h1>
|
|
219
|
+
<span class="brand">Squirrels Project</span>
|
|
220
|
+
{% if project_name %}
|
|
221
|
+
<br>
|
|
222
|
+
<span class="project">{{ project_name }}</span>
|
|
223
|
+
{% endif %}
|
|
224
|
+
</h1>
|
|
225
|
+
<p>Please sign in to continue</p>
|
|
226
|
+
</div>
|
|
227
|
+
|
|
228
|
+
{% if client_name %}
|
|
229
|
+
<div class="client-info">
|
|
230
|
+
<h3>{{ client_name }}</h3>
|
|
231
|
+
<p>wants to access your account</p>
|
|
232
|
+
</div>
|
|
233
|
+
{% endif %}
|
|
234
|
+
|
|
235
|
+
<!-- Username/Password Login Form -->
|
|
236
|
+
<form method="post" action="{{ login_url }}?redirect_url={{ return_url | urlencode }}">
|
|
237
|
+
<div class="form-group">
|
|
238
|
+
<label for="username">Username</label>
|
|
239
|
+
<input type="text" id="username" name="username" required autocomplete="username">
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="form-group">
|
|
243
|
+
<label for="password">Password</label>
|
|
244
|
+
<input type="password" id="password" name="password" required autocomplete="current-password">
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<input type="hidden" name="redirect_url" value="{{ return_url }}">
|
|
248
|
+
|
|
249
|
+
<button type="submit" class="btn btn-primary">
|
|
250
|
+
Sign In
|
|
251
|
+
</button>
|
|
252
|
+
</form>
|
|
253
|
+
|
|
254
|
+
{% if providers and providers|length > 0 %}
|
|
255
|
+
<div class="divider">
|
|
256
|
+
<span>or</span>
|
|
257
|
+
</div>
|
|
258
|
+
|
|
259
|
+
<!-- OAuth Provider Login Options -->
|
|
260
|
+
<div class="providers-section">
|
|
261
|
+
{% for provider in providers %}
|
|
262
|
+
<a href="{{ provider.login_url }}?redirect_url={{ return_url | urlencode }}" class="btn btn-provider">
|
|
263
|
+
<img src="{{ provider.icon }}" alt="{{ provider.label }}" class="provider-icon" onerror="this.style.display='none'">
|
|
264
|
+
<span>Continue with {{ provider.label }}</span>
|
|
265
|
+
</a>
|
|
266
|
+
{% endfor %}
|
|
267
|
+
</div>
|
|
268
|
+
{% endif %}
|
|
269
|
+
</div>
|
|
270
|
+
</body>
|
|
271
|
+
</html>
|
squirrels/_parameter_configs.py
CHANGED
|
@@ -108,7 +108,7 @@ class ParameterConfig(Generic[ParamOptionType], ParameterConfigBase):
|
|
|
108
108
|
pass
|
|
109
109
|
|
|
110
110
|
def _invalid_input_error(self, selection: str, more_details: str = '') -> InvalidInputError:
|
|
111
|
-
return InvalidInputError(
|
|
111
|
+
return InvalidInputError(400, "Invalid parameter selection", f'Selected value "{selection}" is not valid for parameter "{self.name}". ' + more_details)
|
|
112
112
|
|
|
113
113
|
@abstractmethod
|
|
114
114
|
def with_selection(
|
squirrels/_parameter_sets.py
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import Optional, Sequence, Any
|
|
2
|
+
from typing import Optional, Sequence, Callable, Any
|
|
3
3
|
from dataclasses import dataclass, field
|
|
4
4
|
from collections import OrderedDict
|
|
5
5
|
import time, concurrent.futures, polars as pl
|
|
6
6
|
|
|
7
|
-
from . import _parameters as p, _utils as u, _constants as c, _parameter_configs as pc, _py_module as pm
|
|
8
|
-
from .
|
|
7
|
+
from . import _parameters as p, _utils as u, _constants as c, _parameter_configs as pc, _py_module as pm
|
|
8
|
+
from ._schemas import response_models as rm
|
|
9
|
+
from ._arguments.init_time_args import ParametersArgs
|
|
9
10
|
from ._manifest import ParametersConfig, ManifestConfig
|
|
10
|
-
from ._connection_set import ConnectionSet
|
|
11
|
+
from ._connection_set import ConnectionSet
|
|
11
12
|
from ._seeds import Seeds
|
|
12
13
|
from ._auth import BaseUser
|
|
13
14
|
|
|
@@ -22,11 +23,11 @@ class ParameterSet:
|
|
|
22
23
|
def get_parameters_as_dict(self) -> dict[str, p.Parameter]:
|
|
23
24
|
return self._parameters_dict.copy()
|
|
24
25
|
|
|
25
|
-
def to_api_response_model0(self) ->
|
|
26
|
+
def to_api_response_model0(self) -> rm.ParametersModel:
|
|
26
27
|
parameters = []
|
|
27
28
|
for x in self._parameters_dict.values():
|
|
28
29
|
parameters.append(x._to_api_response_model0())
|
|
29
|
-
return
|
|
30
|
+
return rm.ParametersModel(parameters=parameters)
|
|
30
31
|
|
|
31
32
|
|
|
32
33
|
@dataclass
|
|
@@ -153,44 +154,53 @@ class ParameterConfigsSetIO:
|
|
|
153
154
|
"""
|
|
154
155
|
Static class for the singleton object of ParameterConfigsSet
|
|
155
156
|
"""
|
|
156
|
-
|
|
157
|
+
param_factories: list[Callable[[ParametersArgs], pc.ParameterConfigBase]] = [] # this is static (set in load_from_file) to stage the functions from pyconfigs/parameters.py before using them
|
|
157
158
|
|
|
158
159
|
@classmethod
|
|
159
|
-
def _get_df_dict_from_data_sources(
|
|
160
|
+
def _get_df_dict_from_data_sources(
|
|
161
|
+
cls, param_configs_set: ParameterConfigsSet, default_conn_name: str, seeds: Seeds, conn_set: ConnectionSet
|
|
162
|
+
) -> dict[str, pl.DataFrame]:
|
|
163
|
+
|
|
160
164
|
def get_dataframe(ds_param_config: pc.DataSourceParameterConfig) -> tuple[str, pl.DataFrame]:
|
|
161
165
|
return ds_param_config.name, ds_param_config.get_dataframe(default_conn_name, conn_set, seeds)
|
|
162
166
|
|
|
163
|
-
ds_param_configs =
|
|
167
|
+
ds_param_configs = param_configs_set._get_all_ds_param_configs()
|
|
164
168
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
165
169
|
df_dict = dict(executor.map(get_dataframe, ds_param_configs))
|
|
166
170
|
|
|
167
171
|
return df_dict
|
|
168
172
|
|
|
169
173
|
@classmethod
|
|
170
|
-
def _add_from_dict(cls, param_config: ParametersConfig) -> None:
|
|
174
|
+
def _add_from_dict(cls, param_configs_set: ParameterConfigsSet, param_config: ParametersConfig) -> None:
|
|
171
175
|
ptype = getattr(p, param_config.type)
|
|
172
176
|
factory = getattr(ptype, param_config.factory)
|
|
173
|
-
factory(**param_config.arguments)
|
|
174
|
-
|
|
175
|
-
@classmethod
|
|
176
|
-
def get_param_args(cls, conn_args: ConnectionsArgs) -> ParametersArgs:
|
|
177
|
-
return ParametersArgs(conn_args.project_path, conn_args.proj_vars, conn_args.env_vars)
|
|
177
|
+
obj = factory(**param_config.arguments)
|
|
178
|
+
param_configs_set.add(obj)
|
|
178
179
|
|
|
179
180
|
@classmethod
|
|
180
181
|
def load_from_file(
|
|
181
182
|
cls, logger: u.Logger, base_path: str, manifest_cfg: ManifestConfig, seeds: Seeds, conn_set: ConnectionSet, param_args: ParametersArgs
|
|
182
183
|
) -> ParameterConfigsSet:
|
|
183
184
|
start = time.time()
|
|
184
|
-
|
|
185
|
+
param_configs_set = ParameterConfigsSet()
|
|
185
186
|
|
|
186
187
|
for param_as_dict in manifest_cfg.parameters:
|
|
187
|
-
cls._add_from_dict(param_as_dict)
|
|
188
|
+
cls._add_from_dict(param_configs_set, param_as_dict)
|
|
189
|
+
|
|
190
|
+
main_result = pm.run_pyconfig_main(base_path, c.PARAMETERS_FILE, {"sqrl": param_args}) # adds to cls.param_factories as side effect
|
|
191
|
+
param_factories = cls.param_factories
|
|
192
|
+
cls.param_factories = []
|
|
193
|
+
|
|
194
|
+
for param_factory in param_factories:
|
|
195
|
+
param_configs_set.add(param_factory(param_args))
|
|
188
196
|
|
|
189
|
-
|
|
197
|
+
if isinstance(main_result, list):
|
|
198
|
+
for param_config in main_result:
|
|
199
|
+
param_configs_set.add(param_config)
|
|
190
200
|
|
|
191
201
|
default_conn_name = manifest_cfg.env_vars.get(c.SQRL_CONNECTIONS_DEFAULT_NAME_USED, "default")
|
|
192
|
-
df_dict = cls._get_df_dict_from_data_sources(default_conn_name, seeds, conn_set)
|
|
193
|
-
|
|
202
|
+
df_dict = cls._get_df_dict_from_data_sources(param_configs_set, default_conn_name, seeds, conn_set)
|
|
203
|
+
param_configs_set._post_process_params(df_dict)
|
|
194
204
|
|
|
195
205
|
logger.log_activity_time("loading parameters", start)
|
|
196
|
-
return
|
|
206
|
+
return param_configs_set
|